Gradle 运用在组件化中
前面几节我们学习了 Gradle 的任务,命令已经学会了自定义插件。那么下面我们就来学习以下如何将前面所学的 Gradle 知识运用在组件化架构中。我们现在的项目基本都是组件化的架构。但是我们真的了解组件化吗?我们通过这节学习,希望能够帮助到大家在组件化开发中有更高的效率。
1. 组件化与集成化
我们的项目最开始创建时是集成化模式的,但是由于我们一个团队,有很多人在同时开发一个项目,但是大家都负责各自的模块。这样在集成化的模式下,大家要编译跟大家不相关的别的模块相关的代码。所以就出现的组件化模式。在组件化模式下,各个模块可以独立运行。
集成化模式: 就是打包整个项目,编译出一个全业务功能的 apk 文件。各个子模块不能够独立运行,只能依赖于宿主 App。
组件化模式: 就是每个子模块都能够独立运行,不需要依赖宿主 APP 壳。而且每个模块都能够编译出 apk 文件。
2. Android 项目中组件化运用
我们下面来具体看下 Android 项目中我们怎么来实施组件化。我们知道我们发布市场肯定是要打一个全功能的 apk 包,也就是发布市场时是需要集成化的打包模式,而我们开发过程中是需要组件化模式的,所以我们需要一个开关来控制组件化和集成化打包模式。我们各个模块都会有编译工具版本,SDK 的版本,support 库的版本号等。我们可以将这些抽离出来,单独建立一个 Gradle 文件来配置这些全局变量。
2.1 config.gradle
我们创建一个单独的config.gradle
文件,定义全局变量,如下所示:
ext {
// 定义一个项目全局变量isRelease,用于动态切换:组件化模式 / 集成化模式
// false: 组件化模式(子模块可以独立运行),true :集成化模式(打包整个项目apk,子模块不可独立运行)
isRelease = false
// 建立Map存储,对象名、key可以自定义
androidId = [
compileSdkVersion: 28,
buildToolsVersion: "29.0.0",
minSdkVersion : 19,
targetSdkVersion : 28,
versionCode : 1,
versionName : "1.0"
]
appId = ["app" : "com.bthvi.modular",
"order" : "com.bthvi.modular.order",
"personal": "com.bthvi.modular.personal"]
supportLibrary = "28.0.0"
dependencies = [
// ${supportLibrary}表示引用一个变量
"appcompat" : "com.android.support:appcompat-v7:${supportLibrary}",
"recyclerview": "com.android.support:recyclerview-v7:${supportLibrary}",
"constraint" : "com.android.support.constraint:constraint-layout:1.1.3",
"okhttp3" : "com.squareup.okhttp3:okhttp:3.10.0",
"retrofit" : "com.squareup.retrofit2:retrofit:2.5.0",
"fastjson" : "com.alibaba:fastjson:1.2.58",
]
}
2.2 在 build.gradle 中引用 config.gradle
我们要引用我们上面定义的 config.gradle 文件,就需要在项目的根目录下的 build.gradle 中加入以下代码
apply from: "config.gradle"
2.3 在 module 中引用公共变量
前面我们在 config 定义了我们各个模块可能都会用到的依赖库,编译版本,sdk 版本版本号等一些公共变量。下面我们就需要将这些变量在 module 的 build.gradle 中引入。这里我创建了一个项目有 common,order,person 以及主模块 app 四个 module,下面我们以 order 为例。
def rootAndroidId = rootProject.ext.androidId
def appId = rootProject.ext.appId
def support = rootProject.ext.dependencies
android {
compileSdkVersion rootAndroidId.compileSdkVersion
buildToolsVersion rootAndroidId.buildToolsVersion
defaultConfig {
if (!isRelease) { // 如果是集成化模式,不能有applicationId
applicationId appId.order // 组件化模式能独立运行才能有applicationId
}
minSdkVersion rootAndroidId.minSdkVersion
targetSdkVersion rootAndroidId.targetSdkVersion
versionCode rootAndroidId.versionCode
versionName rootAndroidId.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//当前项目的build.config文件里添加了一个boolean类型的变量
buildConfigField("boolean", "isRelease", String.valueOf(isRelease))
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// 循环引入第三方库
support.each { k, v -> implementation v }
implementation project(':common') // 公共基础库
}
Tips:这里我们在前面加入了
buildConfigField
,这个的作用是在当前模块的 build.config 中加入一个 isRelease 的布尔型变量。因为 src 代码中有可能需要用到跨模块交互,如果是组件化模块显然不能跨模块交互的。
2.4 在 module 的 build.gradle 中使用 isRelease 开关
我们引入定义的变量后,我们就需要在 module 中引入组件化开关,这里我们用 isRelease 表示,如果 isRelease 为 true,则表示当前为集成化模式,否则当前为组件化模式,各模块可相互独立。
Tips: 我们知道默认创建项目只有 app 模块才能运行,那么我们现在组件化中需要各个模块都能独立运行,那么我们就需要根据 isRelease 开关来控制了。能够独立运行取决于为 build.gradle 第一行引入的是
com.android.library
还是com.android.application
,只有引入后者module才能独立运行。
根据上面的知识,我们应该在 module 中加入:
if (isRelease) { // 如果是生产发布版本时,各个模块都不能独立运行
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
2.5 配置资源路径
由于组件化和集成化模式中,module 的 AndroidManifest.xml 文件是不同的,组件化时,module 可以独立运行,AndroidManifest.xml 中需要配置 appliation、启动 activity 等。而集成化运行时只有主模块可以配置。所以这里我们就需要这么配置。
// 配置资源路径
sourceSets {
main {
if (!isRelease) {
// 如果是组件化模式,需要单独运行时
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
// 集成化模式,整个项目打包apk
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
2.6 配置组件化测试
我们在开发中可能都会遇到,自己的模块运行需要别的模块的数据,当没有集成别的模块的数据时,我们可以写一些自己的测试数据,或是资源文件等等。就是只有在组件化中能够用到,但是不需要出现在集成化打包后的生产包中的,我们可以单独创建一个文件夹,集合化时使用exclude
不要让这个文件夹合并到项目中。具体如下所示:
// 配置资源路径
sourceSets {
main {
if (!isRelease) {
// 如果是组件化模式,需要单独运行时
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
// 集成化模式,整个项目打包apk
manifest.srcFile 'src/main/AndroidManifest.xml'
java {
// release 时 debug 目录下文件不需要合并到主工程
exclude '**/debug/**'
}
}
}
}
Tips: exclude 的妙用非常多,如果我们一些测试代码,测试数据,或是组件化单独的资源文件我们都可以放在 debug 文件下。编译的时候 exclude 会将这个模块所有的 debug 文件夹下的文件不会合并到整个项目中去。
2.7 完整的 module 的配置
前面我们的配置都是将 order 模块的 build.config,单独拆各个模块来讲的。可能大家有点乱。下面我们看下完整的配置应该是怎样的。
if (isRelease) { // 如果是发布版本时,各个模块都不能独立运行
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
def rootAndroidId = rootProject.ext.androidId
def appId = rootProject.ext.appId
def support = rootProject.ext.dependencies
android {
compileSdkVersion rootAndroidId.compileSdkVersion
buildToolsVersion rootAndroidId.buildToolsVersion
defaultConfig {
if (!isRelease) { // 如果是集成化模式,不能有 applicationId
applicationId appId.order // 组件化模式能独立运行才能有 applicationId
}
minSdkVersion rootAndroidId.minSdkVersion
targetSdkVersion rootAndroidId.targetSdkVersion
versionCode rootAndroidId.versionCode
versionName rootAndroidId.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
buildConfigField("boolean", "isRelease", String.valueOf(isRelease))
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
// 配置资源路径
sourceSets {
main {
if (!isRelease) {
// 如果是组件化模式,需要单独运行时
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
// 集成化模式,整个项目打包apk
manifest.srcFile 'src/main/AndroidManifest.xml'
java {
// release 时 debug 目录下文件不需要合并到主工程
exclude '**/debug/**'
}
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// 循环引入第三方库
support.each { k, v -> implementation v }
implementation project(':common') // 公共基础库
}
3. 小结
本节,我们带大家学习了 Gradle 在 Android 项目组件化中的运用。主要知识点有以下几点:
- 配置公共的依赖 jar 包,编译版本,构建版本,版本号等。
- 用 buildConfigField 将组件化开关添加到 build.config 中,这样 Java 代码也就可以使用组件化开关。
- 配置资源文件,可以将我们的测试资源、代码放在一个单独的目录下,使用 exclude,将这个目录下的文件不合并到项目。