不以規(guī)矩,不成方圓。特別是多人協(xié)作開發(fā)時(shí),如果沒有統(tǒng)一的開發(fā)規(guī)范,勢(shì)必會(huì)造成各種混亂。在實(shí)際開發(fā)中,常常會(huì)碰到的問題有:
- 引入的某個(gè)第三方庫(kù)版本沖突;
- 不同組件里同名資源文件被覆蓋;
- APP殼工程打包時(shí)AndroidManifest.xml合并發(fā)生錯(cuò)誤;
往往單獨(dú)的組件工程運(yùn)行良好,但是集成到殼工程時(shí)就是不行,所以我們必須要嚴(yán)格遵守規(guī)范,盡可能減少這種問題的出現(xiàn)。以下是我在實(shí)際開發(fā)中采用的一些步驟規(guī)范:
1. 新建組件工程
我的每個(gè)組件都是一個(gè)單獨(dú)的小工程,而不是像其他的方案那樣,只有一個(gè)主工程,每個(gè)組件只是工程里的一個(gè)module,這種方式實(shí)質(zhì)上還是單一工程模式。這樣在代碼權(quán)限管控,組件職責(zé)劃分上就很明確了,每個(gè)工程是一個(gè)組件,每個(gè)組件有一個(gè)owner(也就是負(fù)責(zé)人)。
打開Android Studio(目前只采用該IDE來(lái)開發(fā),其他IDE不考慮),點(diǎn)擊"File -> New -> New Project...",全新創(chuàng)建一個(gè)新的工程,工程名字以及包名根據(jù)實(shí)際業(yè)務(wù)來(lái)定。
2. 新建module
在剛創(chuàng)建好的工程中,點(diǎn)擊"File -> New -> New Module... -> Android Library",創(chuàng)建一個(gè)新的library module,接下來(lái)我們所有的組件業(yè)務(wù)代碼都將在該module下面開發(fā)。

如圖所示,我們所有的組件工程都包含2個(gè)module,一個(gè)是app,一個(gè)是library。在library里開發(fā)真正的業(yè)務(wù)功能,在app里只是一個(gè)組件的入口,或者是一些demo測(cè)試代碼。
library module的包名設(shè)置規(guī)則:應(yīng)用包名+ "." + 業(yè)務(wù)模塊名,假設(shè)你的應(yīng)用包名為com.ali.app,你要開發(fā)的業(yè)務(wù)組件為用戶個(gè)人中心,則你的包名可定義為:com.ali.app.userinfo。注意不要與應(yīng)用以及其他業(yè)務(wù)組件的包名發(fā)生沖突,如果你的團(tuán)隊(duì)足夠大,甚至是跨業(yè)務(wù)部門,則組件包名的命名需要格外慎重,你可以加上你部門的代號(hào),這樣別人在使用該組件時(shí),一眼就能知道該組件是誰(shuí)誰(shuí)誰(shuí)開發(fā)的。
3. 資源名命名規(guī)則
所有資源文件的命名都需要以業(yè)務(wù)模塊名為前綴,注意不要與其他業(yè)務(wù)模塊前綴名沖突。假設(shè)我們?cè)陂_發(fā)"登錄"相關(guān)的業(yè)務(wù),業(yè)務(wù)模塊名為"login",則相關(guān)資源文件命名例子:
- layout文件:login_activity_quicklogin.xml、login_activity_register.xml
- anim文件:login_slide_in.xml
- mipmap文件:login_btn_submit.png
- string:<string name="login_submit">提交</string>
包括但不限于以上這些資源文件,所有資源文件的命名都必須遵循該規(guī)則,否則可能在集成的時(shí)候會(huì)被沖突掉。當(dāng)然還有一種方式是在build.gradle文件添加如下配置:
resourcePrefix "module_login"
這個(gè)在打包編譯時(shí)會(huì)自動(dòng)為所有資源加上前綴,但是不管加不加該配置,還是強(qiáng)烈建議資源命名增加前綴。
4. 第三方庫(kù)依賴
在我們使用第三方依賴庫(kù)時(shí),需要特別注意依賴庫(kù)的版本號(hào)。如果第三方依賴庫(kù)在多個(gè)組件中都有使用,考慮將這些第三方依賴下沉到底層組件庫(kù)中統(tǒng)一管理,防止版本號(hào)沖突。
5. 數(shù)據(jù)存儲(chǔ)
- 盡量不要使用數(shù)據(jù)庫(kù)來(lái)存儲(chǔ)數(shù)據(jù),特殊情況除外。某些ORM框架需要數(shù)據(jù)庫(kù)表集中管理,這樣不利于實(shí)行業(yè)務(wù)模塊組件化。
- 使用SharedPreferences時(shí),每個(gè)業(yè)務(wù)模塊只管理自己模塊需要的數(shù)據(jù),SharedPreferences文件名需要通過(guò)業(yè)務(wù)前綴來(lái)區(qū)分,防止不同組件間數(shù)據(jù)發(fā)生沖突。
- 當(dāng)某些數(shù)據(jù)需要全局共享時(shí),可以考慮下沉到底層模塊。
6. 組件生命周期管理
當(dāng)業(yè)務(wù)組件需要在應(yīng)用的Application.onCreate()里進(jìn)行初始化時(shí),我們要在該組件里引入生命周期管理。
7. 發(fā)布本地maven庫(kù)
我的組件都是采用maven進(jìn)行管理的,在殼工程集成時(shí)直接通過(guò)maven引用組件。
前期開發(fā)測(cè)試時(shí),請(qǐng)先在本機(jī)發(fā)布maven庫(kù),這樣方便隨時(shí)修改更新。 在library module的根目錄下新建一個(gè)maven_local.gradle文件:

maven_local.gradle文件的配置如下:
apply plugin: 'maven'
uploadArchives {
repositories.mavenDeployer {
pom.version = '1.0.0'
pom.artifactId = 'loginlocal'
pom.groupId = 'com.hjy.app'
repository(url: "file:///Users/hjy/.m2/repository/"
}
}
- version:maven庫(kù)的版本號(hào),初始版本都從1.0.0開始;
- groupId:maven庫(kù)的組,你自己統(tǒng)一定義;
- artifactId:maven庫(kù)的id,通常為業(yè)務(wù)模塊名,為了與遠(yuǎn)程庫(kù)區(qū)分,本地庫(kù)請(qǐng)加local后綴;
- repository:其中"file:///Users/hjy/.m2/repository/"替換成自己本機(jī)的gradle緩存目錄,在mac中g(shù)radle的緩存目錄路徑為~/.m2/repository/;
在library module的build.gradle里增加發(fā)布腳本的引用"apply from: './maven_push.gradle'",然后點(diǎn)擊"IDE右側(cè)Gradle -> Gradle projects -> 業(yè)務(wù)module -> Tasks -> upload -> uploadArchives",最后會(huì)生成并上傳一個(gè)本地的maven庫(kù)。
在本地測(cè)試時(shí),你可以像下面這樣直接引用本地maven組件庫(kù)了。
在工程根目錄下的build.gradle里增加你本地maven庫(kù)地址:
allprojects {
repositories {
maven { url 'file:///Users/syl/.m2/repository/' }
}
}
然后你就可以直接通過(guò)maven引用你的組件庫(kù)了:
compile 'com.hjy.app:loginlocal:1.0.0'
8. 發(fā)布遠(yuǎn)程maven庫(kù)
如果你的組件庫(kù)測(cè)試通過(guò),最后需要將release版本發(fā)布在遠(yuǎn)程maven服務(wù)器上,在殼工程集成時(shí),采用遠(yuǎn)程依賴的方式。與發(fā)布本地maven庫(kù)相似,在library module的根目錄下新建maven_push.gradle文件,然后在module的build.gradle里,將發(fā)布本地maven庫(kù)的腳本切換成"apply from: './maven_push.gradle'"即可。
apply plugin: 'maven'
apply plugin: 'signing'
configurations {
deployerJars
}
repositories {
mavenCentral()
}
uploadArchives {
repositories {
mavenDeployer {
pom.version = '1.0.0'
pom.artifactId = 'login'
pom.groupId = 'com.hjy.app'
//請(qǐng)改為自己的maven服務(wù)器地址
snapshotRepository(url: 'http://127.0.0.1/nexus/repository/maven-snapshots/') {
authentication(userName: '***', password: '***')
}
repository(url: 'http://127.0.0.1/nexus/repository/maven-releases/') {
authentication(userName: '***', password: '***')
}
}
}
}
// type顯示指定任務(wù)類型或任務(wù), 這里指定要執(zhí)行Javadoc這個(gè)task,這個(gè)task在gradle中已經(jīng)定義
task androidJavadocs(type: Javadoc) {
// 設(shè)置源碼所在的位置
source = android.sourceSets.main.java.sourceFiles
}
// 生成javadoc.jar
task androidJavadocsJar(type: Jar) {
// 指定文檔名稱
classifier = 'javadoc'
from androidJavadocs.destinationDir
}
// 生成sources.jar
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.sourceFiles
}
// 產(chǎn)生相關(guān)配置文件的任務(wù)
artifacts {
archives androidSourcesJar
archives androidJavadocsJar
}
把repository里的url替換成你自己的maven服務(wù)器地址,用戶名密碼換成你自己的maven賬號(hào)即可。
9. 文檔要求
每個(gè)組件都要維護(hù)好說(shuō)明文檔,通常都是一個(gè)readme文件。一般包含以下說(shuō)明:
- 組件的功能介紹;
- 組件怎么集成,以及注意事項(xiàng);
- 組件功能使用說(shuō)明;
- 組件歷史版本記錄;
盡量做到團(tuán)隊(duì)內(nèi)任何一個(gè)成員,通過(guò)該文檔就能使用組件,而不需要找到組件的開發(fā)人員來(lái)講解。
10. 組件集成測(cè)試
前面說(shuō)過(guò),有一種組件開發(fā)模式,其工程結(jié)構(gòu)如下圖所示:

“Helloworld”是我們的應(yīng)用殼工程,“ModuleA”、“ModuleB”、“ModuleC”分別是3個(gè)組件,有一種做法是通過(guò)配置文件,動(dòng)態(tài)改邊某個(gè)Module是library還是application,這樣就可以直接打包調(diào)試運(yùn)行某個(gè)組件(具體做法可以網(wǎng)上搜索)。但是我說(shuō)過(guò),這樣沒法做到代碼權(quán)限管控,也沒法做到開發(fā)人員職責(zé)劃分,每個(gè)開發(fā)人員可以對(duì)任意的組件進(jìn)行修改,這顯然會(huì)造成混亂。所以我摒棄了這種模式,而是把每個(gè)組件都分割成單獨(dú)的工程,集成測(cè)試時(shí),通過(guò)maven引用來(lái)集成。
前面講過(guò),前期自己集成測(cè)試時(shí),先發(fā)布本地maven庫(kù),在殼工程集成時(shí)如下所示:
//注冊(cè)、登錄
compile 'com.hjy.app:loginlocal:1.0.0'
//用戶中心
compile 'com.hjy.app:userinfolocal:1.0.0'
//支付模塊
compile 'com.hjy.app:paylocal:1.0.0'
每個(gè)組件的id都加了一個(gè)local后綴,這是為了區(qū)分我們代碼引用的是本地的maven組件庫(kù),還是遠(yuǎn)程的正式maven組件庫(kù)。
如果測(cè)試時(shí),發(fā)現(xiàn)了bug,只需要把組件庫(kù)本地重新發(fā)布一下,然后殼工程的gradle文件刷新同步一下就可,不用去改版本號(hào),因?yàn)檫@只是你本地的庫(kù),不會(huì)與別人的發(fā)生沖突。
最后測(cè)試通過(guò)之后,我們會(huì)發(fā)布到自己的maven服務(wù)器上,這個(gè)時(shí)候殼工程的build文件如下所示:
//注冊(cè)、登錄
compile 'com.hjy.app:login:1.0.0'
//用戶中心
compile 'com.hjy.app:userinfo:1.0.0'
//支付模塊
compile 'com.hjy.app:pay:1.0.0'
注意,這里組件的id都去掉了local后綴,同時(shí)組件的版本號(hào)必須每發(fā)布一次升一次級(jí),并做好版本變更記錄。
系列文章
Android組件化開發(fā)實(shí)踐(一):為什么要進(jìn)行組件化開發(fā)?
Android組件化開發(fā)實(shí)踐(二):組件化架構(gòu)設(shè)計(jì)
Android組件化開發(fā)實(shí)踐(三):組件開發(fā)規(guī)范
Android組件化開發(fā)實(shí)踐(四):組件間通信問題
Android組件化開發(fā)實(shí)踐(五):組件生命周期管理
Android組件化開發(fā)實(shí)踐(六):老項(xiàng)目實(shí)施組件化
Android組件化開發(fā)實(shí)踐(七):開發(fā)常見問題及解決方案
Android組件化開發(fā)實(shí)踐(八):組件生命周期如何實(shí)現(xiàn)自動(dòng)注冊(cè)管理
Android組件化開發(fā)實(shí)踐(九):自定義Gradle插件
Android組件化開發(fā)實(shí)踐(十):通過(guò)Gradle插件統(tǒng)一規(guī)范