組件化開發(fā)
參考資源
為什么要組件化開發(fā)
解決問題
- 實(shí)際業(yè)務(wù)變化非常快,但是單一工程的業(yè)務(wù)模塊耦合度太高,牽一發(fā)而動(dòng)全身;
- 對(duì)工程所做的任何修改都必須要編譯整個(gè)工程;
- 功能測(cè)試和系統(tǒng)測(cè)試每次都要進(jìn)行;
- 團(tuán)隊(duì)協(xié)同開發(fā)存在較多的沖突.不得不花費(fèi)更多的時(shí)間去溝通和協(xié)調(diào),并且在開發(fā)過(guò)程中,任何一位成員沒辦法專注于自己的功能點(diǎn),影響開發(fā)效率;
- 不能靈活的對(duì)業(yè)務(wù)模塊進(jìn)行配置和組裝;
好處
- 加快業(yè)務(wù)迭代速度,各個(gè)業(yè)務(wù)模塊組件更加獨(dú)立,不再出現(xiàn)業(yè)務(wù)耦合情況;
- 穩(wěn)定的公共模塊采用依賴庫(kù)方式,提供給各個(gè)業(yè)務(wù)線使用,減少重復(fù)開發(fā)和維護(hù)工作量;
- 迭代頻繁的業(yè)務(wù)模塊采用組件方式,各業(yè)務(wù)研發(fā)可以互不干擾、提升協(xié)作效率,并控制產(chǎn)品質(zhì)量;
- 為新業(yè)務(wù)隨時(shí)集成提供了基礎(chǔ),所有業(yè)務(wù)可上可下,靈活多變;
- 降低團(tuán)隊(duì)成員熟悉項(xiàng)目的成本,降低項(xiàng)目的維護(hù)難度;
- 加快編譯速度,提高開發(fā)效率;
- 控制代碼權(quán)限,將代碼的權(quán)限細(xì)分到更小的粒度;
具體實(shí)現(xiàn)方案
1.組件模式和集成模式的轉(zhuǎn)換
android studio 中的Module主要又兩種屬性
-
application屬性,可以單獨(dú)運(yùn)行的Android程序,也就是我們的APP
//這個(gè)是在build.gradle中進(jìn)行設(shè)置的(就是最頂上那一行) apply plugin: 'com.android.application'
-
library屬性,不可以單獨(dú)運(yùn)行,一般是Android程序依賴的庫(kù)文件
//這個(gè)是在build.gradle中進(jìn)行設(shè)置的(就是最頂上那一行) apply plugin: ‘com.android.library’
組件化和集成模式的轉(zhuǎn)換
說(shuō)到這里我不想去講解gradle的語(yǔ)法,因?yàn)槲乙膊粫?huì),但是可以告訴你怎么去弄!!!
這里是引用張華洋的說(shuō)明:博客地址
Module的屬性是在每個(gè)組件的 build.gradle 文件中配置的,當(dāng)我們?cè)诮M件模式開發(fā)時(shí),業(yè)務(wù)組件應(yīng)處于application屬性,這時(shí)的業(yè)務(wù)組件就是一個(gè) Android App,可以獨(dú)立開發(fā)和調(diào)試;而當(dāng)我們轉(zhuǎn)換到集成模式開發(fā)時(shí),業(yè)務(wù)組件應(yīng)該處于 library 屬性,這樣才能被我們的“app殼工程”所依賴,組成一個(gè)具有完整功能的APP;
但是我們?nèi)绾巫尳M件在這兩種模式之間自動(dòng)轉(zhuǎn)換呢?總不能每次需要轉(zhuǎn)換模式的時(shí)候去每個(gè)業(yè)務(wù)組件的 Gralde 文件中去手動(dòng)把 Application 改成 library 吧?如果我們的項(xiàng)目只有兩三個(gè)組件那么這個(gè)辦法肯定是可行的,手動(dòng)去改一遍也用不了多久,但是在大型項(xiàng)目中我們可能會(huì)有十幾個(gè)業(yè)務(wù)組件,再去手動(dòng)改一遍必定費(fèi)時(shí)費(fèi)力,這時(shí)候就需要程序員發(fā)揮下懶的本質(zhì)了。
試想,我們經(jīng)常在寫代碼的時(shí)候定義靜態(tài)常量,那么定義靜態(tài)常量的目的什么呢?當(dāng)一個(gè)常量需要被好幾處代碼引用的時(shí)候,把這個(gè)常量定義為靜態(tài)常量的好處是當(dāng)這個(gè)常量的值需要改變時(shí)我們只需要改變靜態(tài)常量的值,其他引用了這個(gè)靜態(tài)常量的地方都會(huì)被改變,做到了一次改變,到處生效;根據(jù)這個(gè)思想,那么我們就可以在我們的代碼中的某處定義一個(gè)決定業(yè)務(wù)組件屬性的常量,然后讓所有業(yè)務(wù)組件的build.gradle都引用這個(gè)常量,這樣當(dāng)我們改變了常量值的時(shí)候,所有引用了這個(gè)常量值的業(yè)務(wù)組件就會(huì)根據(jù)值的變化改變自己的屬性;可是問題來(lái)了?靜態(tài)常量是用Java代碼定義的,而改變組件屬性是需要在Gradle中定義的,Gradle能做到嗎?
Gradle自動(dòng)構(gòu)建工具有一個(gè)重要屬性,可以幫助我們完成這個(gè)事情。每當(dāng)我們用AndroidStudio創(chuàng)建一個(gè)Android項(xiàng)目后,就會(huì)在項(xiàng)目的根目錄中生成一個(gè)文件 gradle.properties,我們將使用這個(gè)文件的一個(gè)重要屬性:在Android項(xiàng)目中的任何一個(gè)build.gradle文件中都可以把gradle.properties中的常量讀取出來(lái);那么我們?cè)谏厦嫣岬浇鉀Q辦法就有了實(shí)際行動(dòng)的方法,首先我們?cè)趃radle.properties中定義一個(gè)常量值 isModule(是否是組件開發(fā)模式,true為是,false為否):
在gradle.properties中添加是否組件化的判斷
PS:首先gradle.properties這個(gè)文件有一個(gè)重要的功能:在Android項(xiàng)目中的任何一個(gè)build.gradle文件中都可以把gradle.properties中的常量讀取出來(lái)(但是讀取出來(lái)的時(shí)候是String格式的字符串,這一點(diǎn)要切記)
在gradle.properties中添加
# 是否組件化的標(biāo)識(shí),注意啊這里要用"#"號(hào)注釋
isModule = false
然后在相應(yīng)的Module中的build.gradle去添加相應(yīng)的邏輯
//這里說(shuō)明一下就是如果是組件化的
if (isModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
這樣寫完了之后就會(huì)在每次改變*isModule賦值的化就可以更改相應(yīng)的模塊是否能單獨(dú)運(yùn)行了(為了保證單獨(dú)組件的單獨(dú)運(yùn)行,會(huì)創(chuàng)建一個(gè)公用的Module,存放網(wǎng)絡(luò)請(qǐng)求或者圖片處理等一些公共的資源,從而確保module的單獨(dú)運(yùn)行)
組件之間AndroidManifest合并問題
這里是引用張華洋的說(shuō)明:博客地址
但是大家應(yīng)該注意到這個(gè)問題是在組件開發(fā)模式和集成開發(fā)模式之間轉(zhuǎn)換引起的問題,而在上一節(jié)中我們已經(jīng)解決了組件模式和集成模式轉(zhuǎn)換的問題,另外大家應(yīng)該都經(jīng)歷過(guò)將 Android 項(xiàng)目從 Eclipse 切換到 AndroidStudio 的過(guò)程,由于 Android 項(xiàng)目在 Eclipse 和 AndroidStudio開發(fā)時(shí) AndroidManifest.xml 文件的位置是不一樣的,我們需要在build.gradle 中指定下 AndroidManifest.xml 的位置,AndroidStudio 才能讀取到 AndroidManifest.xml,這樣解決辦法也就有了,我們可以為組件開發(fā)模式下的業(yè)務(wù)組件再創(chuàng)建一個(gè) AndroidManifest.xml,然后根據(jù)isModule指定AndroidManifest.xml的文件路徑,讓業(yè)務(wù)組件在集成模式和組件模式下使用不同的AndroidManifest.xml,這樣表單沖突的問題就可以規(guī)避了。
創(chuàng)建一個(gè)新的AndroidManifest.xml
具體的解決方案就是在相應(yīng)的Module中在根路徑添加一個(gè)Module文件夾,直接創(chuàng)建一份AndroidManifest.xml,這里面的這個(gè)文件,直接像你平時(shí)寫項(xiàng)目一樣去寫就可以了
修改相應(yīng)的build.gradle
在android標(biāo)簽內(nèi)創(chuàng)建
sourceSets {
main {
if (isModule.toBoolean()) {/*是組件化*/
manifest.srcFile 'src/main/AndroidManifest.xml'
java {/*集成開發(fā)模式下排除debug文件加中的所有java文件*/
exclude 'debug/**'//這個(gè)相應(yīng)的debug文件是在java文件目錄下的
}
} else {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
}
}
}
全局Context的獲取及組件數(shù)據(jù)初始化
其實(shí)我理解的全局Context的理解就是->先創(chuàng)建一個(gè)所有module都引用的一個(gè)module(common這個(gè)module是每個(gè)module都會(huì)引用的一個(gè)module,這里要處理重復(fù)的問題,后面補(bǔ)充),這個(gè)module就存放一些都能用到的東西,然后引入到module或者主項(xiàng)目中,這樣所有的module都會(huì)包含一些公共的內(nèi)容:如請(qǐng)求網(wǎng)絡(luò),圖片處理等一些相應(yīng)的內(nèi)容,這樣既保證了每個(gè)module的單獨(dú)運(yùn)行,也保證了項(xiàng)目的耦合程度
這里有一個(gè)存在一個(gè)技巧,在前面沒有去理解,就是在組件之間AndroidManifest合并問題處有這樣一段代碼
java {/*集成開發(fā)模式下排除debug文件加中的所有java文件*/
exclude 'debug/**'
}
這段代碼的主要問題就是解決全局Context的問題,為什么呢???
我們想一下場(chǎng)景啊!當(dāng)你某一個(gè)模塊單獨(dú)運(yùn)行的時(shí)候,存在登陸獲取token的問題,但是你要是單獨(dú)運(yùn)行的時(shí)候呢,可能這個(gè)模塊沒有token你怎么辦呢?其實(shí)解決辦法就是創(chuàng)建一個(gè)debug文件夾,這里呢可以自己創(chuàng)建一個(gè)相應(yīng)的Application去請(qǐng)求登陸的接口這樣你的module單獨(dú)運(yùn)行的時(shí)候就會(huì)有token了具體值
(這里我看張華洋的博客時(shí)我覺的處理挺好的,他在公共的類中去創(chuàng)建了一個(gè)BaseApplication所有module中的Application(在debug文件夾下創(chuàng)建的,但是要在清單文件中去配置啊!!!)都去繼承這個(gè)Application,這樣既解決了沒有Application的情況,又解決了集成打包時(shí)候多次創(chuàng)建Application的情況,覺得比較不錯(cuò)所以借鑒一下)
上面那行代碼會(huì)在集成開發(fā)模式的情況下把debug文件夾下的內(nèi)容刪除,這里就成功的解決了單獨(dú)運(yùn)行的token問題,也解決了集成打包的時(shí)候會(huì)出現(xiàn)多個(gè)Application的問題
library依賴問題
解決library的重復(fù)引用問題又兩種方式:
- 根據(jù)組件名稱排除
- 根據(jù)包名排除
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile("com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion") {
exclude module: 'support-v4'//根據(jù)組件名排除
exclude group: 'android.support.v4'//根據(jù)包名排除
}
}
這里說(shuō)明一點(diǎn):基礎(chǔ)的控件一般都在common中去引用,然后住項(xiàng)目和其他的項(xiàng)目都導(dǎo)入common就可以了,但是這里應(yīng)該存在一個(gè)問題就是Common存在導(dǎo)入多次的問題
其實(shí)在構(gòu)建APP的過(guò)程中Gradle會(huì)自動(dòng)將重復(fù)的arr包排除,APP中也就不會(huì)存在相同的代碼了;
組件之間的調(diào)用和通信
這里是引用張華洋的說(shuō)明:博客地址
在組件化開發(fā)的時(shí)候,組件之間是沒有依賴關(guān)系,我們不能在使用顯示調(diào)用來(lái)跳轉(zhuǎn)頁(yè)面了,因?yàn)槲覀兘M件化的目的之一就是解決模塊間的強(qiáng)依賴問題,
假如現(xiàn)在要從A業(yè)務(wù)組件跳轉(zhuǎn)到業(yè)務(wù)B組件,并且要攜帶參數(shù)跳轉(zhuǎn),這時(shí)候怎么辦呢?
而且組件這么多怎么管理也是個(gè)問題,這時(shí)候就需要引入“路由”的概念了,由本文開始的組件化模型下的業(yè)務(wù)關(guān)系圖可知路由就是起到一個(gè)轉(zhuǎn)發(fā)的作用。
這里我將介紹開源庫(kù)的“ActivityRouter” ,有興趣的同學(xué)情直接去ActivityRouter的Github主頁(yè)學(xué)習(xí):ActivityRouter,ActivityRouter支持給Activity定義 URL,
這樣就可以通過(guò) URL 跳轉(zhuǎn)到Activity,并且支持從瀏覽器以及 APP 中跳入我們的Activity,而且還支持通過(guò) url 調(diào)用方法。
下面將介紹如何將ActivityRouter集成到組件化項(xiàng)目中以實(shí)現(xiàn)組件之間的調(diào)用;
這里涉及到路由的問題后續(xù)在進(jìn)行補(bǔ)充
組件之間資源名稱沖突的問題
這里是引用張華洋的說(shuō)明:博客地址
因?yàn)槲覀儾鸱殖隽撕芏鄻I(yè)務(wù)組件和功能組件,在把這些組件合并到“app殼工程”時(shí)候就有可能會(huì)出現(xiàn)資源名沖突問題,
例如A組件和B組件都有一張叫做“ic_back”的圖標(biāo),這時(shí)候在集成模式下打包APP就會(huì)編譯出錯(cuò),解決這個(gè)問題最簡(jiǎn)單的辦法就是在項(xiàng)目中約定資源文件命名規(guī)約,
比如強(qiáng)制使每個(gè)資源文件的名稱以組件名開始,這個(gè)可以根據(jù)實(shí)際情況和開發(fā)人員制定規(guī)則。當(dāng)然了萬(wàn)能的Gradle構(gòu)建工具也提供了解決方法,
通過(guò)在在組件的build.gradle中添加如下的代碼:
```
//設(shè)置了resourcePrefix值后,所有的資源名必須以指定的字符串做前綴,否則會(huì)報(bào)錯(cuò)。
//但是resourcePrefix這個(gè)值只能限定xml里面的資源,并不能限定圖片資源,所有圖片資源仍然需要手動(dòng)去修改資源名。
resourcePrefix "girls_"
```
但是設(shè)置了這個(gè)屬性后有個(gè)問題,所有的資源名必須以指定的字符串做前綴,否則會(huì)報(bào)錯(cuò),
而且resourcePrefix這個(gè)值只能限定xml里面的資源,并不能限定圖片資源,所有圖片資源仍然需要手動(dòng)去修改資源名;
所以我并不推薦使用這種方法來(lái)解決資源名沖突。
2.組件化項(xiàng)目的工程類型
在組件化工程模型中主要有:app殼工程、業(yè)務(wù)組件和功能組件3種類型,而業(yè)務(wù)組件中的Main組件和功能組件中的Common組件比較特殊,下面將分別介紹。
1.app殼工程
app殼工程相當(dāng)于一個(gè)空殼的項(xiàng)目,沒有任何業(yè)務(wù)代碼,也不能又Activity,但是它又必須單獨(dú)劃分一個(gè)組件,而且不能融合其他組件中;
因?yàn)樗謳c(diǎn)重要的功能:
app殼工程中聲明了我們Android應(yīng)用的Application 但是這個(gè)Application必須是繼承Common組件中的BaseApplication(如果無(wú)需事先自己的Application可以直接在表單生命BaseApplication),
因?yàn)橹挥羞@樣,在打包應(yīng)用后才能讓BaseApplication中的Context生效,當(dāng)然你也可以在這個(gè)Application中初始化我們工程中使用的庫(kù)文件,還可以在這里面解決Android引用方法數(shù)超過(guò)65535的限制,
對(duì)于崩潰事件的捕獲和發(fā)送也可以在這里面聲明app殼工程的AndroidManifest.xml是我們Android應(yīng)用的根表單 應(yīng)用的名,圖標(biāo)依稀是否支持備份等等屬性都是在這份表單中配置的,
其他組件中的表單最終在集成開發(fā)模式下都被合并到這份AndroidManifest.xml中app殼工程的build.gradle是比較特殊的 app殼不管是在集成開發(fā)模式還是組件開發(fā)模式,它的屬性始終都是:
com.android.application,因?yàn)樽罱K其它的組件都要被app殼工程所依賴,被打包進(jìn)app殼工程中,這一點(diǎn)從組件化工程模型圖中就能體現(xiàn)出來(lái),
所以app殼工程是不需要單獨(dú)調(diào)試單獨(dú)開發(fā)的.另外Android應(yīng)用的打包簽名,以及build.gradle和defaultConfig都需要在這里面配置,
而它的dependencies則需要根據(jù)isModule的值分別依賴不同的組件,在組件開發(fā)模式下app殼工程只需要依賴Common組件,
或者為了防止報(bào)錯(cuò)也可以根據(jù)實(shí)際情況依賴其他功能組件,而在集成模式下app殼工程必須依賴所有在應(yīng)用Application中聲明的業(yè)務(wù)組件,并且不需要再依賴任何功能組件。
這里是引用張華洋的說(shuō)明:博客地址
一份殼工程的build.gradle文件
apply plugin: 'com.android.application'
static def buildTime() {
return new Date().format("yyyyMMdd");
}
android {
signingConfigs {
release {
keyAlias 'guiying712'
keyPassword 'guiying712'
storeFile file('/mykey.jks')
storePassword 'guiying712'
}
}
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId "com.guiying.androidmodulepattern"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
multiDexEnabled false
//打包時(shí)間
resValue "string", "build_time", buildTime()
}
buildTypes {
release {
//更改AndroidManifest.xml中預(yù)先定義好占位符信息
//manifestPlaceholders = [app_icon: "@drawable/icon"]
// 不顯示Log
buildConfigField "boolean", "LEO_DEBUG", "false"
//是否zip對(duì)齊
zipAlignEnabled true
// 縮減resource文件
shrinkResources true
//Proguard
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//簽名
signingConfig signingConfigs.release
}
debug {
//給applicationId添加后綴“.debug”
applicationIdSuffix ".debug"
//manifestPlaceholders = [app_icon: "@drawable/launch_beta"]
buildConfigField "boolean", "LOG_DEBUG", "true"
zipAlignEnabled false
shrinkResources false
minifyEnabled false
debuggable true
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
annotationProcessor "com.github.mzule.activityrouter:compiler:$rootProject.annotationProcessor"
if (isModule.toBoolean()) {
compile project(':lib_common')
} else {
compile project(':module_main')
compile project(':module_girls')
compile project(':module_news')
}
}
2.功能組件和Common組件
功能組件是為了支撐業(yè)務(wù)組件的某些功能而獨(dú)立劃分出來(lái)的組件,功能組件和第三方庫(kù)是一樣的,
擁有如下特征:
功能組件
功能組件的AndroidManifest.xml是一張空表,這張表只有功能組件的包名;
功能組件不管是在集成開發(fā)還是組件開發(fā)模式下屬性始終是:com.android.library,
所以功能組件不需要讀取gradle.properties 中的isModule值的;另外功能組件的build.gradle也無(wú)需設(shè)置buildType是,
只需要dependencies這個(gè)共鞥組件需要的jar包和開源庫(kù).
這里是引用張華洋的說(shuō)明:博客地址
一份 普通 的功能組件的 build.gradle文件:
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
Common組件
Common組件除了有功能組件的普遍屬性外,還具有其他功能:
- Common組件的 AndroidManifest.xml 不是一張空表,這張表中聲明了我們 Android應(yīng)用用到的所有使用權(quán)限 uses-permission 和 uses-feature,放到這里是因?yàn)樵诮M件開發(fā)模式下,所有業(yè)務(wù)組件就無(wú)需在自己的 AndroidManifest.xm 聲明自己要用到的權(quán)限了。
- Common組件的 build.gradle 需要統(tǒng)一依賴業(yè)務(wù)組件中用到的 第三方依賴庫(kù)和jar包,例如我們用到的ActivityRouter、Okhttp等等。
- Common組件中封裝了Android應(yīng)用的 Base類和網(wǎng)絡(luò)請(qǐng)求工具、圖片加載工具等等,公用的 widget控件也應(yīng)該放在Common 組件中;業(yè)務(wù)組件中都用到的數(shù)據(jù)也應(yīng)放于Common組件中,例如保存到 SharedPreferences 和 DataBase 中的登陸數(shù)據(jù);
- Common組件的資源文件中需要放置項(xiàng)目公用的 Drawable、layout、sting、dimen、color和style 等等,另外項(xiàng)目中的 Activity 主題必須定義在 Common中,方便和 BaseActivity 配合保持整個(gè)Android應(yīng)用的界面風(fēng)格統(tǒng)一。
這里是引用張華洋的說(shuō)明:博客地址
一份 Common 的功能組件的 build.gradle文件:
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
//Android Support
compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
compile "com.android.support:design:$rootProject.supportLibraryVersion"
compile "com.android.support:percent:$rootProject.supportLibraryVersion"
//網(wǎng)絡(luò)請(qǐng)求相關(guān)
compile "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"
compile "com.squareup.retrofit2:retrofit-mock:$rootProject.retrofitVersion"
compile "com.github.franmontiel:PersistentCookieJar:$rootProject.cookieVersion"
//穩(wěn)定的
compile "com.github.bumptech.glide:glide:$rootProject.glideVersion"
compile "com.orhanobut:logger:$rootProject.loggerVersion"
compile "org.greenrobot:eventbus:$rootProject.eventbusVersion"
compile "com.google.code.gson:gson:$rootProject.gsonVersion"
compile "com.github.chrisbanes:PhotoView:$rootProject.photoViewVersion"
compile "com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion"
compile "com.github.GrenderG:Toasty:$rootProject.toastyVersion"
//router
compile "com.github.mzule.activityrouter:activityrouter:$rootProject.routerVersion"
}
3.業(yè)務(wù)組件和Main組件
業(yè)務(wù)組件
業(yè)務(wù)組件就是根據(jù)業(yè)務(wù)邏輯的不同拆分出來(lái)的組件,業(yè)務(wù)組件的特征如下:
- 業(yè)務(wù)組件中要有兩張AndroidManifest.xml,分別對(duì)應(yīng)組件開發(fā)模式和集成開發(fā)模式,這兩張表的區(qū)別請(qǐng)查看 組件之間AndroidManifest合并問題 小節(jié)。
- 業(yè)務(wù)組件在集成模式下是不能有自己的Application的,但在組件開發(fā)模式下又必須實(shí)現(xiàn)自己的Application并且要繼承自Common組件的BaseApplication,并且這個(gè)Application不能被業(yè)務(wù)組件中的代碼引用,因?yàn)樗墓δ芫褪菫榱耸箻I(yè)務(wù)組件從BaseApplication中獲取的全局Context生效,還有初始化數(shù)據(jù)之用。
- 業(yè)務(wù)組件有debug文件夾,這個(gè)文件夾在集成模式下會(huì)從業(yè)務(wù)組件的代碼中排除掉,所以debug文件夾中的類不能被業(yè)務(wù)組件強(qiáng)引用,例如組件模式下的 Application 就是置于這個(gè)文件夾中,還有組件模式下開發(fā)給目標(biāo) Activity 傳遞參數(shù)的用的 launch Activity 也應(yīng)該置于 debug 文件夾中;
- 業(yè)務(wù)組件必須在自己的 Java文件夾中創(chuàng)建業(yè)務(wù)組件聲明類,以使 app殼工程 中的 應(yīng)用Application能夠引用,實(shí)現(xiàn)組件跳轉(zhuǎn),具體請(qǐng)查看 組件之間調(diào)用和通信 小節(jié);
- 業(yè)務(wù)組件必須在自己的 build.gradle 中根據(jù) isModule 值的不同改變自己的屬性,在組件模式下是:com.android.application,而在集成模式下com.android.library;同時(shí)還需要在build.gradle配置資源文件,如 指定不同開發(fā)模式下的AndroidManifest.xml文件路徑,排除debug文件夾等;業(yè)務(wù)組件還必須在dependencies中依賴Common組件,并且引入ActivityRouter的注解處理器annotationProcessor,以及依賴其他用到的功能組件。
這里是引用張華洋的說(shuō)明:博客地址
一份普通業(yè)務(wù)組件的 build.gradle文件:
if (isModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
}
sourceSets {
main {
if (isModule.toBoolean()) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
//集成開發(fā)模式下排除debug文件夾中的所有Java文件
java {
exclude 'debug/**'
}
}
}
}
//設(shè)置了resourcePrefix值后,所有的資源名必須以指定的字符串做前綴,否則會(huì)報(bào)錯(cuò)。
//但是resourcePrefix這個(gè)值只能限定xml里面的資源,并不能限定圖片資源,所有圖片資源仍然需要手動(dòng)去修改資源名。
//resourcePrefix "girls_"
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
annotationProcessor "com.github.mzule.activityrouter:compiler:$rootProject.annotationProcessor"
compile project(':lib_common')
}
Main組件
Main組件集成模式下的AndroidManifest.xml是跟其他業(yè)務(wù)組件不一樣的,Main組件的表單中聲明了我們整個(gè)Android應(yīng)用的launch Activity,這就是Main組件的獨(dú)特之處;所以我建議SplashActivity、登陸Activity以及主界面都應(yīng)屬于Main組件,也就是說(shuō)Android應(yīng)用啟動(dòng)后要調(diào)用的頁(yè)面應(yīng)置于Main組件。
這里是引用張華洋的說(shuō)明:博客地址
一份Main業(yè)務(wù)組件的 build.gradle文件:
<activity
android:name=".splash.SplashActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
3.組件化項(xiàng)目的混淆方案
組件化項(xiàng)目的Java代碼混淆方案采用在集成模式下集中在app殼工程中混淆,各個(gè)業(yè)務(wù)組件不配置混淆文件。 集成開發(fā)模式下在app殼工程中build.gradle文件的release構(gòu)建類型中開啟混淆屬性,其他buildTypes配置方案跟普通項(xiàng)目保持一致,Java混淆配置文件也放置在app殼工程中,各個(gè)業(yè)務(wù)組件的混淆配置規(guī)則都應(yīng)該在app殼工程中的混淆配置文件中添加和修改。
之所以不采用在每個(gè)業(yè)務(wù)組件中開啟混淆的方案,是因?yàn)?組件在集成模式下都被 Gradle 構(gòu)建成了 release 類型的arr包,一旦業(yè)務(wù)組件的代碼被混淆,而這時(shí)候代碼中又出現(xiàn)了bug,將很難根據(jù)日志找出導(dǎo)致bug的原因;另外每個(gè)業(yè)務(wù)組件中都保留一份混淆配置文件非常不便于修改和管理,這也是不推薦在業(yè)務(wù)組件的 build.gradle 文件中配置 buildTypes (構(gòu)建類型)的原因。
4.工程的build.gradle和gradle.properties文件
組件化工程的build.gradle文件
在組件化項(xiàng)目中因?yàn)槊總€(gè)組件的 build.gradle 都需要配置 compileSdkVersion、buildToolsVersion和defaultConfig 等的版本號(hào),而且每個(gè)組件都需要用到 annotationProcessor,為了能夠使組件化項(xiàng)目中的所有組件的 build.gradle 中的這些配置都能保持統(tǒng)一,并且也是為了方便修改版本號(hào),我們統(tǒng)一在Android工程根目錄下的build.gradle中定義這些版本號(hào),當(dāng)然為了方便管理Common組件中的第三方開源庫(kù)的版本號(hào),最好也在這里定義這些開源庫(kù)的版本號(hào),然后在各個(gè)組件的build.gradle中引用Android工程根目錄下的build.gradle定義的版本號(hào),組件化工程的 build.gradle 文件代碼如下:
這里是引用張華洋的說(shuō)明:博客地址
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
//classpath "com.android.tools.build:gradle:$localGradlePluginVersion"
//$localGradlePluginVersion是gradle.properties中的數(shù)據(jù)
classpath "com.android.tools.build:gradle:$localGradlePluginVersion"
}
}
allprojects {
repositories {
jcenter()
mavenCentral()
//Add the JitPack repository
maven { url "https://jitpack.io" }
//支持arr包
flatDir {
dirs 'libs'
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
// Define versions in a single place
//時(shí)間:2017.2.13;每次修改版本號(hào)都要添加修改時(shí)間
ext {
// Sdk and tools
//localBuildToolsVersion是gradle.properties中的數(shù)據(jù)
buildToolsVersion = localBuildToolsVersion
compileSdkVersion = 25
minSdkVersion = 16
targetSdkVersion = 25
versionCode = 1
versionName = "1.0"
javaVersion = JavaVersion.VERSION_1_8
// App dependencies version
supportLibraryVersion = "25.3.1"
retrofitVersion = "2.1.0"
glideVersion = "3.7.0"
loggerVersion = "1.15"
eventbusVersion = "3.0.0"
gsonVersion = "2.8.0"
photoViewVersion = "2.0.0"
//需檢查升級(jí)版本
annotationProcessor = "1.1.7"
routerVersion = "1.2.2"
easyRecyclerVersion = "4.4.0"
cookieVersion = "v1.0.1"
toastyVersion = "1.1.3"
}
組件化工程的gradle.properties文件
在組件化實(shí)施流程中我們了解到gradle.properties有兩個(gè)屬性對(duì)我們非常有用:
- 在Android項(xiàng)目中的任何一個(gè)build.gradle文件中都可以把gradle.properties中的常量讀取出來(lái),不管這個(gè)build.gradle是組件的還是整個(gè)項(xiàng)目工程的build.gradle;
- gradle.properties中的數(shù)據(jù)類型都是String類型,使用其他數(shù)據(jù)類型需要自行轉(zhuǎn)換;
利用gradle.properties的屬性不僅可以解決集成開發(fā)模式和組件開發(fā)模式的轉(zhuǎn)換,而且還可以解決在多人協(xié)同開發(fā)Android項(xiàng)目的時(shí)候,因?yàn)殚_發(fā)團(tuán)隊(duì)成員的Android開發(fā)環(huán)境(開發(fā)環(huán)境指Android SDK和AndroidStudio)不一致而導(dǎo)致頻繁改變線上項(xiàng)目的build.gradle配置。
在每個(gè)Android組件的 build.gradle 中有一個(gè)屬性:buildToolsVersion,表示構(gòu)建工具的版本號(hào),這個(gè)屬性值對(duì)應(yīng) AndroidSDK 中的 Android SDK Build-tools,正常情況下 build.gradle 中的 buildToolsVersion 跟你電腦中 Android SDK Build-tools 的最新版本是一致的,比如現(xiàn)在 Android SDK Build-tools 的最新的版本是:25.0.3,那么我的Android項(xiàng)目中 build.gradle 中的 buildToolsVersion 版本號(hào)也是 25.0.3,但是一旦一個(gè)Android項(xiàng)目是由好幾個(gè)人同時(shí)開發(fā),總會(huì)出現(xiàn)每個(gè)人的開發(fā)環(huán)境 Android SDK Build-tools 是都是不一樣的,并不是所有人都會(huì)經(jīng)常升級(jí)更新 Android SDK,而且代碼是保存到線上環(huán)境的(例如使用 SVN/Git 等工具),某個(gè)開發(fā)人員提交代碼后線上Android項(xiàng)目中 build.gradle 中的 buildToolsVersion 也會(huì)被不斷地改變。
另外一個(gè)原因是因?yàn)锳ndroid工程的根目錄下的 build.gradle 聲明了 Android Gradle 構(gòu)建工具,而這個(gè)工具也是有版本號(hào)的,而且 Gradle Build Tools 的版本號(hào)跟 AndroidStudio 版本號(hào)一致的,但是有些開發(fā)人員基本很久都不會(huì)升級(jí)自己的 AndroidStudio 版本,導(dǎo)致團(tuán)隊(duì)中每個(gè)開發(fā)人員的 Gradle Build Tools 的版本號(hào)也不一致。
如果每次同步代碼后這兩個(gè)工具的版本號(hào)被改變了,開發(fā)人員可以自己手動(dòng)改回來(lái),并且不要把改動(dòng)工具版本號(hào)的代碼提交到線上環(huán)境,這樣還可以勉強(qiáng)繼續(xù)開發(fā);但是很多公司都會(huì)使用持續(xù)集成工具(例如Jenkins)用于持續(xù)的軟件版本發(fā)布,而Android出包是需要 Android SDK Build-tools 和 Gradle Build Tools 配合的,一旦提交到線上的版本跟持續(xù)集成工具所依賴的Android環(huán)境構(gòu)建工具版本號(hào)不一致就會(huì)導(dǎo)致Android打包失敗。
為了解決上面問題就必須將Android項(xiàng)目中 build.gradle 中的 buildToolsVersion 和 GradleBuildTools 版本號(hào)從線上代碼隔離出來(lái),保證線上代碼的 buildToolsVersion 和 Gradle Build Tools 版本號(hào)不會(huì)被人為改變。
具體的實(shí)施流程大家可以查看這篇博文: AndroidStudio本地化配置gradle的buildToolsVersion和gradleBuildTools
上面這些要感謝章華洋的博客,基本是按照他的寫法自己寫了一遍,體會(huì)了一下,里面的只是還是很多了,僅作備份,如果樓主有意見,馬上刪除!!!