从零开始Android组件化改造(二) - Gradle的管理
我的Github:https://github.com/BzCoder
欢迎各位留言讨论
Gradle的管理在组件化改造中是一个非常有学问的环节。在我看来Gradle在其中的主要几个职责:
- 引入包的版本管理
- 组件化编译与总体编译的切换
- 各模块间的层级关系维护
- gradle.properties 配置中转站
接下来我们就一点一点的讲。
1.引入包的版本管理
这其实不是组件化开发的专利。正如其他的项目一样,统一的版本号我们都管理在Config.gradle中。类似下面的文件。在模块中引入模块时,统一通过类似api rootProject.ext.dependencies["mmkv"]
的引入方式来保证版本的统一。这个很好理解,因为都是常规操作。
ext {
android = [
applicationId : "${PACKAGE_NAME}",
compileSdkVersion: 28,
buildToolsVersion: "28.0.3",
minSdkVersion : 18,
targetSdkVersion : 28,
versionCode : VERSION_CODE as int,
versionName : "${VERSION_NAME}"
]
version = [
androidSupportSdkVersion: "28.0.0",
retrofitSdkVersion : "2.4.0",
dagger2SdkVersion : "2.19",
]
dependencies = [
//support
"appcompat-v7" : "com.android.support:appcompat-v7:${version["androidSupportSdkVersion"]}",
"design" : "com.android.support:design:${version["androidSupportSdkVersion"]}",
"support-v4" : "com.android.support:support-v4:${version["androidSupportSdkVersion"]}",
"cardview-v7" : "com.android.support:cardview-v7:${version["androidSupportSdkVersion"]}",
"annotations" : "com.android.support:support-annotations:${version["androidSupportSdkVersion"]}",
"recyclerview-v7" : "com.android.support:recyclerview-v7:${version["androidSupportSdkVersion"]}",
"constraint-layout" : 'com.android.support.constraint:constraint-layout:1.1.3',
]
defaultConfig {
minSdkVersion rootProject.ext.android["minSdkVersion"]
targetSdkVersion rootProject.ext.android["targetSdkVersion"]
versionCode rootProject.ext.android["versionCode"]
versionName rootProject.ext.android["versionName"]
testInstrumentationRunner rootProject.ext.dependencies["androidJUnitRunner"]
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
includeCompileClasspath true
}
}
multiDexEnabled true
}
2.组件化编译与总体编译的切换
2.1 App与Lib
我们知道,lib模块需要引入apply plugin: 'com.android.library'
,应用模块引入apply plugin: 'com.android.application'
,而在组件化开发中,这两种状态是要不断地切换的,所以我们可以在gradle.properties设定参数isBuildModule来控制。于是gradle的头部就变成了以下
if (isBuildModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
当然在实际开发中我们设计了多层,相应的我们也给每层建立了基础gradle,参数也随之变成了isBuildModuleOne,isBuildModuleTwo,isBuildModuleThree...每一层的业务只要依赖该层基础gradle即可。
2.2 两套Manifest
组件化编译和整体编译,他们的清单文件也是不同的。我们通过在Gradle中android节点中加入以下代码来控制工程使用两套Manifest,但是这种方案美中不足的是,你的每个Activity的注册都必须要写两次
sourceSets {
main {
jniLibs.srcDirs = ['libs']
if (isBuildModule.toBoolean()) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/release/AndroidManifest.xml'
}
}
}
2.3 不同编译方式时导入不同的包
说到包的导入,我们先必须明确两个关键字的概念。
- runtimeOnly:只有在运行时才编译。
- compileOnly:只有编译的时候引入,实际打包时并不会加入到包当中。
根据以上原则
在模块互相导入时,我们使用如下格式:
if (moduleTv.toBoolean()) {
runtimeOnly project(":moduleTv")
}
这样写,首先(if语句)可以通过配置文件来确认某个模块是否加入,使用runtimeOnly ,可以在某一个Module组件化编译时,不会影响其他引用它的模块。
在通用模块导入时,我们使用如下格式:
butterknife,dagger2等的包引入。
if (isBuildModule.toBoolean()) {
//view
annotationProcessor(rootProject.ext.dependencies["butterknife-compiler"]) {
exclude module: 'support-annotations'
}
//tools
annotationProcessor rootProject.ext.dependencies["dagger2-compiler"]
annotationProcessor rootProject.ext.dependencies["arouter-compiler"]
//test
debugImplementation rootProject.ext.dependencies["canary-debug"]
releaseImplementation rootProject.ext.dependencies["canary-release"]
testImplementation rootProject.ext.dependencies["canary-release"]
} else {
compileOnly rootProject.ext.dependencies["butterknife-compiler"]
compileOnly rootProject.ext.dependencies["dagger2-compiler"]
compileOnly rootProject.ext.dependencies["arouter-compiler"]
compileOnly rootProject.ext.dependencies["canary-debug"]
compileOnly rootProject.ext.dependencies["canary-release"]
}
这样写只有在组件化编译的时候,模块才会真正把基础包引入,整体打包时,只有一份。
3.Manifest与gradle.properties的数据桥梁
因为我们目标想把所有的配置文件都整合到gradle.properties中,但是manifest又不能直接调用gradle.properties中的参数,我们就必须借助gradle作为“中间人”。我们在gradle的defaultconfig节点中加入(以下为推送的写法)
manifestPlaceholders = [
GETUI_APP_ID : "${PUSH_APPID}",
GETUI_APP_KEY : "${PUSH_APPKEY}",
GETUI_APP_SECRET: "${PUSH_APPSECRET}"
]
这样一来,在Manifest中就可以取到gradle.properties 的参数了。
总结
Gradle的配置总的来说不是太难,以上方案也存在可以优化的地方,就是Manifest,携程曾经有一篇文件是利用manifest的tools:node ="remove"来进行manifest的合并,但是很可惜,在我自己实践的过程中,遇到了不少困难,最终没有采用携程的方案。好的,那么今天的文章就先暂时写到这里。