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,單獨拆各個模塊來講的??赡艽蠹矣悬c亂。下面我們看下完整的配置應該是怎樣的。
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,將這個目錄下的文件不合并到項目。