Gradle 運(yùn)用在組件化中
前面幾節(jié)我們學(xué)習(xí)了 Gradle 的任務(wù),命令已經(jīng)學(xué)會(huì)了自定義插件。那么下面我們就來學(xué)習(xí)以下如何將前面所學(xué)的 Gradle 知識(shí)運(yùn)用在組件化架構(gòu)中。我們現(xiàn)在的項(xiàng)目基本都是組件化的架構(gòu)。但是我們真的了解組件化嗎?我們通過這節(jié)學(xué)習(xí),希望能夠幫助到大家在組件化開發(fā)中有更高的效率。
1. 組件化與集成化
我們的項(xiàng)目最開始創(chuàng)建時(shí)是集成化模式的,但是由于我們一個(gè)團(tuán)隊(duì),有很多人在同時(shí)開發(fā)一個(gè)項(xiàng)目,但是大家都負(fù)責(zé)各自的模塊。這樣在集成化的模式下,大家要編譯跟大家不相關(guān)的別的模塊相關(guān)的代碼。所以就出現(xiàn)的組件化模式。在組件化模式下,各個(gè)模塊可以獨(dú)立運(yùn)行。
集成化模式: 就是打包整個(gè)項(xiàng)目,編譯出一個(gè)全業(yè)務(wù)功能的 apk 文件。各個(gè)子模塊不能夠獨(dú)立運(yùn)行,只能依賴于宿主 App。
組件化模式: 就是每個(gè)子模塊都能夠獨(dú)立運(yùn)行,不需要依賴宿主 APP 殼。而且每個(gè)模塊都能夠編譯出 apk 文件。
2. Android 項(xiàng)目中組件化運(yùn)用
我們下面來具體看下 Android 項(xiàng)目中我們?cè)趺磥韺?shí)施組件化。我們知道我們發(fā)布市場(chǎng)肯定是要打一個(gè)全功能的 apk 包,也就是發(fā)布市場(chǎng)時(shí)是需要集成化的打包模式,而我們開發(fā)過程中是需要組件化模式的,所以我們需要一個(gè)開關(guān)來控制組件化和集成化打包模式。我們各個(gè)模塊都會(huì)有編譯工具版本,SDK 的版本,support 庫的版本號(hào)等。我們可以將這些抽離出來,單獨(dú)建立一個(gè) Gradle 文件來配置這些全局變量。
2.1 config.gradle
我們創(chuàng)建一個(gè)單獨(dú)的config.gradle
文件,定義全局變量,如下所示:
ext {
// 定義一個(gè)項(xiàng)目全局變量isRelease,用于動(dòng)態(tài)切換:組件化模式 / 集成化模式
// false: 組件化模式(子模塊可以獨(dú)立運(yùn)行),true :集成化模式(打包整個(gè)項(xiàng)目apk,子模塊不可獨(dú)立運(yùn)行)
isRelease = false
// 建立Map存儲(chǔ),對(duì)象名、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}表示引用一個(gè)變量
"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 文件,就需要在項(xiàng)目的根目錄下的 build.gradle 中加入以下代碼
apply from: "config.gradle"
2.3 在 module 中引用公共變量
前面我們?cè)?config 定義了我們各個(gè)模塊可能都會(huì)用到的依賴庫,編譯版本,sdk 版本版本號(hào)等一些公共變量。下面我們就需要將這些變量在 module 的 build.gradle 中引入。這里我創(chuàng)建了一個(gè)項(xiàng)目有 common,order,person 以及主模塊 app 四個(gè) 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 // 組件化模式能獨(dú)立運(yùn)行才能有applicationId
}
minSdkVersion rootAndroidId.minSdkVersion
targetSdkVersion rootAndroidId.targetSdkVersion
versionCode rootAndroidId.versionCode
versionName rootAndroidId.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//當(dāng)前項(xiàng)目的build.config文件里添加了一個(gè)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'])
// 循環(huán)引入第三方庫
support.each { k, v -> implementation v }
implementation project(':common') // 公共基礎(chǔ)庫
}
Tips:這里我們?cè)谇懊婕尤肓?code>buildConfigField,這個(gè)的作用是在當(dāng)前模塊的 build.config 中加入一個(gè) isRelease 的布爾型變量。因?yàn)?src 代碼中有可能需要用到跨模塊交互,如果是組件化模塊顯然不能跨模塊交互的。
2.4 在 module 的 build.gradle 中使用 isRelease 開關(guān)
我們引入定義的變量后,我們就需要在 module 中引入組件化開關(guān),這里我們用 isRelease 表示,如果 isRelease 為 true,則表示當(dāng)前為集成化模式,否則當(dāng)前為組件化模式,各模塊可相互獨(dú)立。
Tips: 我們知道默認(rèn)創(chuàng)建項(xiàng)目只有 app 模塊才能運(yùn)行,那么我們現(xiàn)在組件化中需要各個(gè)模塊都能獨(dú)立運(yùn)行,那么我們就需要根據(jù) isRelease 開關(guān)來控制了。能夠獨(dú)立運(yùn)行取決于為 build.gradle 第一行引入的是
com.android.library
還是com.android.application
,只有引入后者module才能獨(dú)立運(yùn)行。
根據(jù)上面的知識(shí),我們應(yīng)該在 module 中加入:
if (isRelease) { // 如果是生產(chǎn)發(fā)布版本時(shí),各個(gè)模塊都不能獨(dú)立運(yùn)行
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
2.5 配置資源路徑
由于組件化和集成化模式中,module 的 AndroidManifest.xml 文件是不同的,組件化時(shí),module 可以獨(dú)立運(yùn)行,AndroidManifest.xml 中需要配置 appliation、啟動(dòng) activity 等。而集成化運(yùn)行時(shí)只有主模塊可以配置。所以這里我們就需要這么配置。
// 配置資源路徑
sourceSets {
main {
if (!isRelease) {
// 如果是組件化模式,需要單獨(dú)運(yùn)行時(shí)
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
// 集成化模式,整個(gè)項(xiàng)目打包apk
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
2.6 配置組件化測(cè)試
我們?cè)陂_發(fā)中可能都會(huì)遇到,自己的模塊運(yùn)行需要?jiǎng)e的模塊的數(shù)據(jù),當(dāng)沒有集成別的模塊的數(shù)據(jù)時(shí),我們可以寫一些自己的測(cè)試數(shù)據(jù),或是資源文件等等。就是只有在組件化中能夠用到,但是不需要出現(xiàn)在集成化打包后的生產(chǎn)包中的,我們可以單獨(dú)創(chuàng)建一個(gè)文件夾,集合化時(shí)使用exclude
不要讓這個(gè)文件夾合并到項(xiàng)目中。具體如下所示:
// 配置資源路徑
sourceSets {
main {
if (!isRelease) {
// 如果是組件化模式,需要單獨(dú)運(yùn)行時(shí)
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
// 集成化模式,整個(gè)項(xiàng)目打包apk
manifest.srcFile 'src/main/AndroidManifest.xml'
java {
// release 時(shí) debug 目錄下文件不需要合并到主工程
exclude '**/debug/**'
}
}
}
}
Tips: exclude 的妙用非常多,如果我們一些測(cè)試代碼,測(cè)試數(shù)據(jù),或是組件化單獨(dú)的資源文件我們都可以放在 debug 文件下。編譯的時(shí)候 exclude 會(huì)將這個(gè)模塊所有的 debug 文件夾下的文件不會(huì)合并到整個(gè)項(xiàng)目中去。
2.7 完整的 module 的配置
前面我們的配置都是將 order 模塊的 build.config,單獨(dú)拆各個(gè)模塊來講的??赡艽蠹矣悬c(diǎn)亂。下面我們看下完整的配置應(yīng)該是怎樣的。
if (isRelease) { // 如果是發(fā)布版本時(shí),各個(gè)模塊都不能獨(dú)立運(yùn)行
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 // 組件化模式能獨(dú)立運(yùn)行才能有 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) {
// 如果是組件化模式,需要單獨(dú)運(yùn)行時(shí)
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
// 集成化模式,整個(gè)項(xiàng)目打包apk
manifest.srcFile 'src/main/AndroidManifest.xml'
java {
// release 時(shí) debug 目錄下文件不需要合并到主工程
exclude '**/debug/**'
}
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// 循環(huán)引入第三方庫
support.each { k, v -> implementation v }
implementation project(':common') // 公共基礎(chǔ)庫
}
3. 小結(jié)
本節(jié),我們帶大家學(xué)習(xí)了 Gradle 在 Android 項(xiàng)目組件化中的運(yùn)用。主要知識(shí)點(diǎn)有以下幾點(diǎn):
- 配置公共的依賴 jar 包,編譯版本,構(gòu)建版本,版本號(hào)等。
- 用 buildConfigField 將組件化開關(guān)添加到 build.config 中,這樣 Java 代碼也就可以使用組件化開關(guān)。
- 配置資源文件,可以將我們的測(cè)試資源、代碼放在一個(gè)單獨(dú)的目錄下,使用 exclude,將這個(gè)目錄下的文件不合并到項(xiàng)目。