android 組件化

Android組件化項(xiàng)目地址:Android組件化項(xiàng)目AndroidModulePattern

Android組件化之終極方案地址:http://blog.csdn.net/guiying712/article/details/78057120

1為什么要項(xiàng)目組件化

2如何組件化

3組件化實(shí)施流程

1組件模式和集成模式的轉(zhuǎn)換

2組件之間AndroidManifest合并問題

3全局Context的獲取及組件數(shù)據(jù)初始化

4library依賴問題

5組件之間調(diào)用和通信

6組件之間資源名沖突

4組件化項(xiàng)目的工程類型

1app殼工程

2功能組件和Common組件

2業(yè)務(wù)組件和Main組件

5組件化項(xiàng)目的混淆方案

6工程的buildgradle和gradleproperties文件

1組件化工程的buildgradle文件

2組件化工程的gradleproperties文件

7組件化項(xiàng)目Router的其他方案-ARouter

8結(jié)束語

1、為什么要項(xiàng)目組件化

隨著APP版本不斷的迭代,新功能的不斷增加妙色,業(yè)務(wù)也會(huì)變的越來越復(fù)雜锈玉,APP業(yè)務(wù)模塊的數(shù)量有可能還會(huì)繼續(xù)增加贤徒,而且每個(gè)模塊的代碼也變的越來越多醉蚁,這樣發(fā)展下去單一工程下的APP架構(gòu)勢(shì)必會(huì)影響開發(fā)效率睛琳,增加項(xiàng)目的維護(hù)成本盒蟆,每個(gè)工程師都要熟悉如此之多的代碼,將很難進(jìn)行多人協(xié)作開發(fā)师骗,而且Android項(xiàng)目在編譯代碼的時(shí)候電腦會(huì)非忱龋卡,又因?yàn)閱我还こ滔麓a耦合嚴(yán)重辟癌,每修改一處代碼后都要重新編譯打包測(cè)試寒屯,導(dǎo)致非常耗時(shí),最重要的是這樣的代碼想要做單元測(cè)試根本無從下手黍少,所以必須要有更靈活的架構(gòu)代替過去單一的工程架構(gòu)浩螺。

上圖是目前比較普遍使用的Android APP技術(shù)架構(gòu),往往是在一個(gè)界面中存在大量的業(yè)務(wù)邏輯仍侥,而業(yè)務(wù)邏輯中充斥著各種網(wǎng)絡(luò)請(qǐng)求要出、數(shù)據(jù)操作等行為,整個(gè)項(xiàng)目中也沒有模塊的概念农渊,只有簡(jiǎn)單的以業(yè)務(wù)邏輯劃分的文件夾患蹂,并且業(yè)務(wù)之間也是直接相互調(diào)用、高度耦合在一起的砸紊;

上圖單一工程模型下的業(yè)務(wù)關(guān)系传于,總的來說就是:你中有我,我中有你醉顽,相互依賴沼溜,無法分離。?

然而隨著產(chǎn)品的迭代游添,業(yè)務(wù)越來越復(fù)雜系草,隨之帶來的是項(xiàng)目結(jié)構(gòu)復(fù)雜度的極度增加,此時(shí)我們會(huì)面臨如下幾個(gè)問題:

1唆涝、實(shí)際業(yè)務(wù)變化非痴叶迹快,但是單一工程的業(yè)務(wù)模塊耦合度太高廊酣,牽一發(fā)而動(dòng)全身能耻;?

2、對(duì)工程所做的任何修改都必須要編譯整個(gè)工程;?

3晓猛、功能測(cè)試和系統(tǒng)測(cè)試每次都要進(jìn)行饿幅;?

4、團(tuán)隊(duì)協(xié)同開發(fā)存在較多的沖突.不得不花費(fèi)更多的時(shí)間去溝通和協(xié)調(diào)戒职,并且在開發(fā)過程中栗恩,任何一位成員沒辦法專注于自己的功能點(diǎn),影響開發(fā)效率帕涌;?

5摄凡、不能靈活的對(duì)業(yè)務(wù)模塊進(jìn)行配置和組裝;

為了滿足各個(gè)業(yè)務(wù)模塊的迭代而彼此不受影響蚓曼,更好的解決上面這種讓人頭疼的依賴關(guān)系亲澡,就需要整改App的架構(gòu)。

2纫版、如何組件化

上圖是組件化工程模型床绪,為了方便理解這張架構(gòu)圖,下面會(huì)列舉一些組件化工程中用到的名詞的含義:

名詞含義

集成模式所有的業(yè)務(wù)組件被“app殼工程”依賴其弊,組成一個(gè)完整的APP癞己;

組件模式可以獨(dú)立開發(fā)業(yè)務(wù)組件,每一個(gè)業(yè)務(wù)組件就是一個(gè)APP梭伐;

app殼工程負(fù)責(zé)管理各個(gè)業(yè)務(wù)組件痹雅,和打包apk,沒有具體的業(yè)務(wù)功能糊识;

業(yè)務(wù)組件根據(jù)公司具體業(yè)務(wù)而獨(dú)立形成一個(gè)的工程绩社;

功能組件提供開發(fā)APP的某些基礎(chǔ)功能,例如打印日志赂苗、樹狀圖等愉耙;

Main組件屬于業(yè)務(wù)組件,指定APP啟動(dòng)頁面拌滋、主界面朴沿;

Common組件屬于功能組件,支撐業(yè)務(wù)組件的基礎(chǔ)败砂,提供多數(shù)業(yè)務(wù)組件需要的功能赌渣,例如提供網(wǎng)絡(luò)請(qǐng)求功能;

**?

Android APP組件化架構(gòu)的目標(biāo)是告別結(jié)構(gòu)臃腫吠卷,讓各個(gè)業(yè)務(wù)變得相對(duì)獨(dú)立锡垄,業(yè)務(wù)組件在組件模式下可以獨(dú)立開發(fā),而在集成模式下又可以變?yōu)閍rr包集成到“app殼工程”中祭隔,組成一個(gè)完整功能的APP;?

從組件化工程模型中可以看到,業(yè)務(wù)組件之間是獨(dú)立的疾渴,沒有關(guān)聯(lián)的千贯,這些業(yè)務(wù)組件在集成模式下是一個(gè)個(gè)library,被app殼工程所依賴搞坝,組成一個(gè)具有完整業(yè)務(wù)功能的APP應(yīng)用搔谴,但是在組件開發(fā)模式下,業(yè)務(wù)組件又變成了一個(gè)個(gè)application桩撮,它們可以獨(dú)立開發(fā)和調(diào)試敦第,由于在組件開發(fā)模式下,業(yè)務(wù)組件們的代碼量相比于完整的項(xiàng)目差了很遠(yuǎn)店量,因此在運(yùn)行時(shí)可以顯著減少編譯時(shí)間芜果。

這是組件化工程模型下的業(yè)務(wù)關(guān)系,業(yè)務(wù)之間將不再直接引用和依賴融师,而是通過“路由”這樣一個(gè)中轉(zhuǎn)站間接產(chǎn)生聯(lián)系右钾,而Android中的路由實(shí)際就是對(duì)URL Scheme的封裝;?

如此規(guī)模大的架構(gòu)整改需要付出更高的成本旱爆,還會(huì)涉及一些潛在的風(fēng)險(xiǎn)舀射,但是整改后的架構(gòu)能夠帶來很多好處:

1、加快業(yè)務(wù)迭代速度怀伦,各個(gè)業(yè)務(wù)模塊組件更加獨(dú)立脆烟,不再出現(xiàn)業(yè)務(wù)耦合情況;?

2房待、穩(wěn)定的公共模塊采用依賴庫方式邢羔,提供給各個(gè)業(yè)務(wù)線使用,減少重復(fù)開發(fā)和維護(hù)工作量吴攒;?

3张抄、迭代頻繁的業(yè)務(wù)模塊采用組件方式,各業(yè)務(wù)研發(fā)可以互不干擾洼怔、提升協(xié)作效率署惯,并控制產(chǎn)品質(zhì)量;?

4镣隶、為新業(yè)務(wù)隨時(shí)集成提供了基礎(chǔ)极谊,所有業(yè)務(wù)可上可下,靈活多變安岂;?

5轻猖、降低團(tuán)隊(duì)成員熟悉項(xiàng)目的成本,降低項(xiàng)目的維護(hù)難度域那;?

6咙边、加快編譯速度,提高開發(fā)效率;?

7败许、控制代碼權(quán)限王带,將代碼的權(quán)限細(xì)分到更小的粒度;

3市殷、組件化實(shí)施流程

1)組件模式和集成模式的轉(zhuǎn)換

Android Studio中的Module主要有兩種屬性愕撰,分別為:

1、application屬性醋寝,可以獨(dú)立運(yùn)行的Android程序搞挣,也就是我們的APP;

apply plugin: ‘com.android.application’

1

2音羞、library屬性囱桨,不可以獨(dú)立運(yùn)行,一般是Android程序依賴的庫文件黄选;

apply plugin: ‘com.android.library’

1

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ù)值的變化改變自己的屬性覆致;可是問題來了侄旬?靜態(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中的常量讀取出來;那么我們?cè)谏厦嫣岬浇鉀Q辦法就有了實(shí)際行動(dòng)的方法族操,首先我們?cè)趃radle.properties中定義一個(gè)常量值?isModule(是否是組件開發(fā)模式苛坚,true為是,false為否)

# 每次更改“isModule”的值后色难,需要點(diǎn)擊"Sync Project"按鈕isModule=false

然后我們?cè)跇I(yè)務(wù)組件的build.gradle中讀取?isModule泼舱,但是 gradle.properties 還有一個(gè)重要屬性:?gradle.properties 中的數(shù)據(jù)類型都是String類型,使用其他數(shù)據(jù)類型需要自行轉(zhuǎn)換枷莉;也就是說我們讀到 isModule 是個(gè)String類型的值娇昙,而我們需要的是Boolean值,代碼如下:

if(isModule.toBoolean()) {? ? apply plugin:'com.android.application'}else{? ? apply plugin:'com.android.library'}

這樣我們第一個(gè)問題就解決了笤妙,當(dāng)然了?每次改變isModule的值后冒掌,都要同步項(xiàng)目才能生效;

2)組件之間AndroidManifest合并問題

在 AndroidStudio 中每一個(gè)組件都會(huì)有對(duì)應(yīng)的 AndroidManifest.xml蹲盘,用于聲明需要的權(quán)限股毫、Application、Activity召衔、Service铃诬、Broadcast等,當(dāng)項(xiàng)目處于組件模式時(shí)薄嫡,業(yè)務(wù)組件的 AndroidManifest.xml 應(yīng)該具有一個(gè) Android APP 所具有的的所有屬性氧急,尤其是聲明 Application 和要 launch的Activity,但是當(dāng)項(xiàng)目處于集成模式的時(shí)候毫深,每一個(gè)業(yè)務(wù)組件的 AndroidManifest.xml 都要合并到“app殼工程”中吩坝,要是每一個(gè)業(yè)務(wù)組件都有自己的 Application 和 launch的Activity,那么合并的時(shí)候肯定會(huì)沖突哑蔫,試想一個(gè)APP怎么可能會(huì)有多個(gè) Application 和 launch 的Activity呢钉寝?

但是大家應(yīng)該注意到這個(gè)問題是在組件開發(fā)模式和集成開發(fā)模式之間轉(zhuǎn)換引起的問題弧呐,而在上一節(jié)中我們已經(jīng)解決了組件模式和集成模式轉(zhuǎn)換的問題,另外大家應(yīng)該都經(jīng)歷過將 Android 項(xiàng)目從 Eclipse 切換到 AndroidStudio 的過程嵌纲,由于 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ī)避了。

上圖是組件化項(xiàng)目中一個(gè)標(biāo)準(zhǔn)的業(yè)務(wù)組件目錄結(jié)構(gòu)墓臭,首先我們?cè)趍ain文件夾下創(chuàng)建一個(gè)module文件夾用于存放組件開發(fā)模式下業(yè)務(wù)組件的 AndroidManifest.xml蘸鲸,而 AndroidStudio 生成的 AndroidManifest.xml 則依然保留,并用于集成開發(fā)模式下業(yè)務(wù)組件的表單窿锉;然后我們需要在業(yè)務(wù)組件的 build.gradle 中指定表單的路徑酌摇,代碼如下:

sourceSets {? ? ? ? main {if(isModule.toBoolean()) {? ? ? ? ? ? ? ? manifest.srcFile'src/main/module/AndroidManifest.xml'}else{? ? ? ? ? ? ? ? manifest.srcFile'src/main/AndroidManifest.xml'}? ? ? ? }? ? }

這樣在不同的開發(fā)模式下就會(huì)讀取到不同的 AndroidManifest.xml ,然后我們需要修改這兩個(gè)表單的內(nèi)容以為我們不同的開發(fā)模式服務(wù)嗡载。

首先是集成開發(fā)模式下的 AndroidManifest.xml窑多,前面我們說過集成模式下,業(yè)務(wù)組件的表單是絕對(duì)不能擁有自己的 Application 和 launch 的 Activity的鼻疮,也不能聲明APP名稱怯伊、圖標(biāo)等屬性,總之a(chǎn)pp殼工程有的屬性判沟,業(yè)務(wù)組件都不能有耿芹,下面是一份標(biāo)準(zhǔn)的集成開發(fā)模式下業(yè)務(wù)組件的 AndroidManifest.xml:

我在這個(gè)表單中只聲明了應(yīng)用的主題,而且這個(gè)主題還是跟app殼工程中的主題是一致的挪哄,都引用了common組件中的資源文件吧秕,在這里聲明主題是為了方便這個(gè)業(yè)務(wù)組件中有使用默認(rèn)主題的Activity時(shí)就不用再給Activity單獨(dú)聲明theme了。

然后是組件開發(fā)模式下的表單文件:

組件模式下的業(yè)務(wù)組件表單就是一個(gè)Android項(xiàng)目普通的AndroidManifest.xml迹炼,這里就不在過多介紹了砸彬。

3)全局Context的獲取及組件數(shù)據(jù)初始化

當(dāng)Android程序啟動(dòng)時(shí),Android系統(tǒng)會(huì)為每個(gè)程序創(chuàng)建一個(gè) Application 類的對(duì)象斯入,并且只創(chuàng)建一個(gè)砂碉,application對(duì)象的生命周期是整個(gè)程序中最長的,它的生命周期就等于這個(gè)程序的生命周期刻两。在默認(rèn)情況下應(yīng)用系統(tǒng)會(huì)自動(dòng)生成 Application 對(duì)象增蹭,但是如果我們自定義了 Application,那就需要在 AndroidManifest.xml 中聲明告知系統(tǒng)磅摹,實(shí)例化的時(shí)候滋迈,是實(shí)例化我們自定義的霎奢,而非默認(rèn)的。

但是我們?cè)诮M件化開發(fā)的時(shí)候饼灿,可能為了數(shù)據(jù)的問題每一個(gè)組件都會(huì)自定義一個(gè)Application類幕侠,如果我們?cè)谧约旱慕M件中開發(fā)時(shí)需要獲取?全局的Context,一般都會(huì)直接獲取 application 對(duì)象碍彭,但是當(dāng)所有組件要打包合并在一起的時(shí)候就會(huì)出現(xiàn)問題晤硕,因?yàn)樽詈蟪绦蛑挥幸粋€(gè) Application,我們組件中自己定義的 Application 肯定是沒法使用的硕旗,因此我們需要想辦法再任何一個(gè)業(yè)務(wù)組件中都能獲取到全局的 Context窗骑,而且這個(gè) Context 不管是在組件開發(fā)模式還是在集成開發(fā)模式都是生效的。

在 組件化工程模型圖中漆枚,功能組件集合中有一個(gè)?Common 組件, Common 有公共抵知、公用墙基、共同的意思,所以這個(gè)組件中主要封裝了項(xiàng)目中需要的基礎(chǔ)功能刷喜,并且每一個(gè)業(yè)務(wù)組件都要依賴Common組件残制,Common 組件就像是萬丈高樓的地基,而業(yè)務(wù)組件就是在 Common 組件這個(gè)地基上搭建起來我們的APP的掖疮,Common 組件會(huì)專門在一個(gè)章節(jié)中講解初茶,這里只講 Common組件中的一個(gè)功能,在Common組件中我們封裝了項(xiàng)目中用到的各種Base類浊闪,這些基類中就有BaseApplication 類恼布。

BaseApplication 主要用于各個(gè)業(yè)務(wù)組件和app殼工程中聲明的 Application 類繼承用的,只要各個(gè)業(yè)務(wù)組件和app殼工程中聲明的Application類繼承了 BaseApplication搁宾,當(dāng)應(yīng)用啟動(dòng)時(shí) BaseApplication 就會(huì)被動(dòng)實(shí)例化折汞,這樣從 BaseApplication 獲取的 Context 就會(huì)生效,也就從根本上解決了我們不能直接從各個(gè)組件獲取全局 Context 的問題盖腿;

這時(shí)候大家肯定都會(huì)有個(gè)疑問爽待?不是說了業(yè)務(wù)組件不能有自己的 Application 嗎,怎么還讓他們繼承 BaseApplication 呢翩腐?其實(shí)我前面說的是業(yè)務(wù)組件不能在集成模式下?lián)碛凶约旱?Application鸟款,但是這不代表業(yè)務(wù)組件也不能在組件開發(fā)模式下?lián)碛凶约旱腁pplication,其實(shí)業(yè)務(wù)組件在組件開發(fā)模式下必須要有自己的 Application 類茂卦,一方面是為了讓 BaseApplication 被實(shí)例化從而獲取 Context何什,還有一個(gè)作用是,業(yè)務(wù)組件自己的 Application 可以在組件開發(fā)模式下初始化一些數(shù)據(jù)疙筹,例如在組件開發(fā)模式下富俄,A組件沒有登錄頁面也沒法登錄禁炒,因此就無法獲取到 Token,這樣請(qǐng)求網(wǎng)絡(luò)就無法成功霍比,因此我們需要在A組件這個(gè) APP 啟動(dòng)后就應(yīng)該已經(jīng)登錄了幕袱,這時(shí)候組件自己的 Application 類就有了用武之地,我們?cè)诮M件的 Application的 onCreate 方法中模擬一個(gè)登陸接口悠瞬,在登陸成功后將數(shù)據(jù)保存到本地们豌,這樣就可以處理A組件中的數(shù)據(jù)業(yè)務(wù)了;另外我們也可以在組件Application中初始化一些第三方庫浅妆。

但是望迎,實(shí)際上業(yè)務(wù)組件中的Application在最終的集成項(xiàng)目中是沒有什么實(shí)際作用的,組件自己的 Application 僅限于在組件模式下發(fā)揮功能凌外,因此我們需要在將項(xiàng)目從組件模式轉(zhuǎn)換到集成模式后將組件自己的Application剔除出我們的項(xiàng)目辩尊;在 AndroidManifest 合并問題小節(jié)中介紹了如何在不同開發(fā)模式下讓 Gradle 識(shí)別組件表單的路徑,這個(gè)方法也同樣適用于Java代碼康辑;

我們?cè)贘ava文件夾下創(chuàng)建一個(gè) debug 文件夾摄欲,用于存放不會(huì)在業(yè)務(wù)組件中引用的類,例如上圖中的 NewsApplication 疮薇,你甚至可以在 debug 文件夾中創(chuàng)建一個(gè)Activity胸墙,然后組件表單中聲明啟動(dòng)這個(gè)Activity,在這個(gè)Activity中不用setContentView按咒,只需要在啟動(dòng)你的目標(biāo)Activity的時(shí)候傳遞參數(shù)就行迟隅,這樣就就可以解決組件模式下某些Activity需要getIntent數(shù)據(jù)而沒有辦法拿到的情況,代碼如下励七;

publicclassLauncherActivityextendsAppCompatActivity{@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);? ? ? ? request();? ? ? ? Intent intent =newIntent(this, TargetActivity.class);? ? ? ? intent.putExtra("name","avcd");? ? ? ? intent.putExtra("syscode","023e2e12ed");? ? ? ? startActivity(intent);? ? ? ? finish();? ? }//申請(qǐng)讀寫權(quán)限privatevoidrequest() {? ? ? ? AndPermission.with(this)? ? ? ? ? ? ? ? .requestCode(110)? ? ? ? ? ? ? ? .permission(Manifest.permission.WRITE_EXTERNAL_STORAGE,? ? ? ? ? ? ? ? ? ? ? ? Manifest.permission.CAMERA, Manifest.permission.READ_PHONE_STATE)? ? ? ? ? ? ? ? .callback(this)? ? ? ? ? ? ? ? .start();? ? }}

接下來在業(yè)務(wù)組件的 build.gradle 中智袭,根據(jù) isModule 是否是集成模式將 debug 這個(gè) Java代碼文件夾排除:

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/**'}? ? ? ? ? ? }? ? ? ? }? ? }

4)library依賴問題

在介紹這一節(jié)的時(shí)候,先說一個(gè)問題呀伙,在組件化工程模型圖中补履,多媒體組件和Common組件都依賴了日志組件,而A業(yè)務(wù)組件有同時(shí)依賴了多媒體組件和Common組件剿另,這時(shí)候就會(huì)有人問箫锤,你這樣搞豈不是日志組件要被重復(fù)依賴了,而且Common組件也被每一個(gè)業(yè)務(wù)組件依賴了雨女,這樣不出問題嗎谚攒?

其實(shí)大家完全沒有必要擔(dān)心這個(gè)問題,如果真有重復(fù)依賴的問題氛堕,在你編譯打包的時(shí)候就會(huì)報(bào)錯(cuò)馏臭,如果你還是不相信的話可以反編譯下最后打包出來的APP,看看里面的代碼你就知道了。組件只是我們?cè)诖a開發(fā)階段中為了方便叫的一個(gè)術(shù)語括儒,在組件被打包進(jìn)APP的時(shí)候是沒有這個(gè)概念的绕沈,這些組件最后都會(huì)被打包成arr包,然后被app殼工程所依賴帮寻,在構(gòu)建APP的過程中Gradle會(huì)自動(dòng)將重復(fù)的arr包排除乍狐,APP中也就不會(huì)存在相同的代碼了;

但是雖然組件是不會(huì)重復(fù)了固逗,但是我們還是要考慮另一個(gè)情況浅蚪,我們?cè)赽uild.gradle中compile的第三方庫,例如AndroidSupport庫經(jīng)常會(huì)被一些開源的控件所依賴烫罩,而我們自己一定也會(huì)compile AndroidSupport庫 惜傲,這就會(huì)造成第三方包和我們自己的包存在重復(fù)加載,解決辦法就是找出那個(gè)多出來的庫贝攒,并將多出來的庫給排除掉盗誊,而且Gradle也是支持這樣做的,分別有兩種方式:根據(jù)組件名排除或者根據(jù)包名排除饿这,下面以排除support-v4庫為例:

dependencies {? ? compile fileTree(dir:'libs', include: ['*.jar'])? ? compile("com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion") {? ? ? ? exclude module:'support-v4'//根據(jù)組件名排除exclude group:'android.support.v4'//根據(jù)包名排除}}

library重復(fù)依賴的問題算是都解決了浊伙,但是我們?cè)陂_發(fā)項(xiàng)目的時(shí)候會(huì)依賴很多開源庫,而這些庫每個(gè)組件都需要用到长捧,要是每個(gè)組件都去依賴一遍也是很麻煩的,尤其是給這些庫升級(jí)的時(shí)候车猬,為了方便我們統(tǒng)一管理第三方庫尝江,我們將給給整個(gè)工程提供統(tǒng)一的依賴第三方庫的入口阵难,前面介紹的Common庫的作用之一就是統(tǒng)一依賴開源庫,因?yàn)槠渌麡I(yè)務(wù)組件都依賴了Common庫肌割,所以這些業(yè)務(wù)組件也就間接依賴了Common所依賴的開源庫。

dependencies {? ? compile fileTree(dir:'libs', include: ['*.jar'])//Android Supportcompile"com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"compile"com.android.support:design:$rootProject.supportLibraryVersion"compile"com.android.support:percent:$rootProject.supportLibraryVersion"http://網(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"http://穩(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"http://routercompile"com.github.mzule.activityrouter:activityrouter:$rootProject.routerVersion"}

5)組件之間調(diào)用和通信

在組件化開發(fā)的時(shí)候帐要,組件之間是沒有依賴關(guān)系把敞,我們不能在使用顯示調(diào)用來跳轉(zhuǎn)頁面了,因?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ā)的作用期揪。

這里我將介紹開源庫的“ActivityRouter”?掉奄,有興趣的同學(xué)情直接去ActivityRouter的Github主頁學(xué)習(xí):ActivityRouter,ActivityRouter支持給Activity定義 URL凤薛,這樣就可以通過 URL 跳轉(zhuǎn)到Activity姓建,并且支持從瀏覽器以及 APP 中跳入我們的Activity诞仓,而且還支持通過 url 調(diào)用方法。下面將介紹如何將ActivityRouter集成到組件化項(xiàng)目中以實(shí)現(xiàn)組件之間的調(diào)用速兔;

1墅拭、首先我們需要在 Common 組件中的 build.gradle 將ActivityRouter 依賴進(jìn)來,方便我們?cè)跇I(yè)務(wù)組件中調(diào)用:

dependencies {? ? compile fileTree(dir:'libs', include: ['*.jar'])//routercompile"com.github.mzule.activityrouter:activityrouter:$rootProject.routerVersion"}

2憨栽、這一步我們需要先了解?APT這個(gè)概念帜矾,APT(Annotation Processing Tool)是一種處理注解的工具,它對(duì)源代碼文件進(jìn)行檢測(cè)找出其中的Annotation屑柔,使用Annotation進(jìn)行額外的處理屡萤。 Annotation處理器在處理Annotation時(shí)可以根據(jù)源文件中的Annotation生成額外的源文件和其它的文件(文件具體內(nèi)容由Annotation處理器的編寫者決定),APT還會(huì)編譯生成的源文件和原來的源文件掸宛,將它們一起生成class文件死陆。在這里我們將在每一個(gè)業(yè)務(wù)組件的 build.gradle 都引入ActivityRouter 的 Annotation處理器,我們將會(huì)在聲明組件和Url的時(shí)候使用唧瘾,annotationProcessor是Android官方提供的Annotation處理器插件措译,代碼如下:

dependencies {? ? compile fileTree(dir:'libs', include: ['*.jar'])? ? annotationProcessor"com.github.mzule.activityrouter:compiler:$rootProject.annotationProcessor"}

3、接下來需要在?app殼工程的 AndroidManifest.xml 配置饰序,到這里ActivityRouter配置就算完成了:

4领虹、接下來我們將聲明項(xiàng)目中的業(yè)務(wù)組件,聲明方法如下:

@Module("girls")publicclassGirls{}

在每一個(gè)業(yè)務(wù)組件的java文件的根目錄下創(chuàng)建一個(gè)類求豫,用?注解@Module?聲明這個(gè)業(yè)務(wù)組件塌衰;?

然后在“app殼工程”的?應(yīng)用Application?中使用?注解@Modules?管理我們聲明的所有業(yè)務(wù)組件,方法如下:

@Modules({"main","girls","news"})publicclassMyApplicationextendsBaseApplication{}

到這里組件化項(xiàng)目中的所有業(yè)務(wù)組件就被聲明和管理起來了蝠嘉,組件之間的也就可以互相調(diào)用了最疆,當(dāng)然前提是要給業(yè)務(wù)組件中的Activity定義 URL。

5蚤告、例如我們給 Girls組件 中的 GirlsActivity 使用?注解@Router?定義一個(gè) URL:“news”努酸,方法如下:

@Router("girls")publicclassGirlsActivityextendsBaseActionBarActivity{privateGirlsView mView;privateGirlsContract.Presenter mPresenter;@OverrideprotectedintsetTitleId() {returnR.string.girls_activity_title;? ? }@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);? ? ? ? mView =newGirlsView(this);? ? ? ? setContentView(mView);? ? ? ? mPresenter =newGirlsPresenter(mView);? ? ? ? mPresenter.start();? ? }}

然后我們就可以在項(xiàng)目中的任何一個(gè)地方通過 URL地址 : module://girls, 調(diào)用 GirlsActivity,方法如下:

Routers.open(MainActivity.this,"module://girls");

組件之間的調(diào)用解決后杜恰,另外需要解決的就是組件之間的通信获诈,例如A業(yè)務(wù)組件中有消息列表,而用戶在B組件中操作某個(gè)事件后會(huì)產(chǎn)生一條新消息箫章,需要通知A組件刷新消息列表烙荷,這樣業(yè)務(wù)場(chǎng)景需求可以使用Android廣播來解決,也可以使用第三方的事件總線來實(shí)現(xiàn)檬寂,比如EventBus终抽。

6)組件之間資源名沖突

因?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)然了萬能的Gradle構(gòu)建工具也提供了解決方法,通過在在組件的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)去修改資源名;所以我并不推薦使用這種方法來解決資源名沖突譬嚣。

4钢颂、組件化項(xiàng)目的工程類型

在組件化工程模型中主要有:app殼工程、業(yè)務(wù)組件和功能組件3種類型拜银,而業(yè)務(wù)組件中的Main組件和功能組件中的Common組件比較特殊甸陌,下面將分別介紹。

1)app殼工程

app殼工程是從名稱來解釋就是一個(gè)空殼工程盐股,沒有任何的業(yè)務(wù)代碼,也不能有Activity耻卡,但它又必須被單獨(dú)劃分成一個(gè)組件疯汁,而不能融合到其他組件中,是因?yàn)樗腥缦聨c(diǎn)重要功能:

1卵酪、app殼工程中聲明了我們Android應(yīng)用的 Application幌蚊,這個(gè) Application 必須繼承自 Common組件中的 BaseApplication(如果你無需實(shí)現(xiàn)自己的Application可以直接在表單聲明BaseApplication),因?yàn)橹挥羞@樣溃卡,在打包應(yīng)用后才能讓BaseApplication中的Context生效溢豆,當(dāng)然你還可以在這個(gè) Application中初始化我們工程中使用到的庫文件,還可以在這里解決Android引用方法數(shù)不能超過 65535 的限制瘸羡,對(duì)崩潰事件的捕獲和發(fā)送也可以在這里聲明漩仙。

2、app殼工程的 AndroidManifest.xml 是我Android應(yīng)用的根表單,應(yīng)用的名稱队他、圖標(biāo)以及是否支持備份等等屬性都是在這份表單中配置的卷仑,其他組件中的表單最終在集成開發(fā)模式下都被合并到這份 AndroidManifest.xml 中。

3麸折、app殼工程的 build.gradle 是比較特殊的锡凝,app殼不管是在集成開發(fā)模式還是組件開發(fā)模式,它的屬性始終都是:com.android.application垢啼,因?yàn)樽罱K其他的組件都要被app殼工程所依賴窜锯,被打包進(jìn)app殼工程中,這一點(diǎn)從組件化工程模型圖中就能體現(xiàn)出來芭析,所以app殼工程是不需要單獨(dú)調(diào)試單獨(dú)開發(fā)的锚扎。另外Android應(yīng)用的打包簽名,以及buildTypes和defaultConfig都需要在這里配置放刨,而它的dependencies則需要根據(jù)isModule的值分別依賴不同的組件工秩,在組件開發(fā)模式下app殼工程只需要依賴Common組件,或者為了防止報(bào)錯(cuò)也可以根據(jù)實(shí)際情況依賴其他功能組件进统,而在集成模式下app殼工程必須依賴所有在應(yīng)用Application中聲明的業(yè)務(wù)組件助币,并且不需要再依賴任何功能組件。

下面是一份 app殼工程 的 build.gradle文件

apply plugin:'com.android.application'staticdef buildTime() {returnnewDate().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? ? ? ? multiDexEnabledfalse//打包時(shí)間resValue"string","build_time", buildTime()? ? }? ? buildTypes {? ? ? ? release {//更改AndroidManifest.xml中預(yù)先定義好占位符信息//manifestPlaceholders = [app_icon: "@drawable/icon"]// 不顯示LogbuildConfigField"boolean","LEO_DEBUG","false"http://是否zip對(duì)齊zipAlignEnabledtrue// 縮減resource文件shrinkResourcestrue//ProguardminifyEnabledtrueproguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'//簽名signingConfig signingConfigs.release? ? ? ? }? ? ? ? debug {//給applicationId添加后綴“.debug”applicationIdSuffix".debug"http://manifestPlaceholders = [app_icon: "@drawable/launch_beta"]buildConfigField"boolean","LOG_DEBUG","true"zipAlignEnabledfalseshrinkResourcesfalseminifyEnabledfalsedebuggabletrue}? ? }}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ú)立劃分出來的組件螟碎,功能實(shí)質(zhì)上跟項(xiàng)目中引入的第三方庫是一樣的眉菱,功能組件的特征如下:

1、功能組件的 AndroidManifest.xml 是一張空表掉分,這張表中只有功能組件的包名俭缓;

2、功能組件不管是在集成開發(fā)模式下還是組件開發(fā)模式下屬性始終是: com.android.library酥郭,所以功能組件是不需要讀取 gradle.properties 中的 isModule 值的华坦;另外功能組件的 build.gradle 也無需設(shè)置 buildTypes ,只需要 dependencies 這個(gè)功能組件需要的jar包和開源庫不从。

下面是一份 普通 的功能組件的 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組件除了有功能組件的普遍屬性外惜姐,還具有其他功能

1、Common組件的 AndroidManifest.xml 不是一張空表椿息,這張表中聲明了我們 Android應(yīng)用用到的所有使用權(quán)限 uses-permission 和 uses-feature歹袁,放到這里是因?yàn)樵诮M件開發(fā)模式下,所有業(yè)務(wù)組件就無需在自己的 AndroidManifest.xm 聲明自己要用到的權(quán)限了寝优。

2条舔、Common組件的 build.gradle 需要統(tǒng)一依賴業(yè)務(wù)組件中用到的 第三方依賴庫和jar包,例如我們用到的ActivityRouter乏矾、Okhttp等等孟抗。

3迁杨、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ù)帆喇;

4警医、Common組件的資源文件中需要放置項(xiàng)目公用的 Drawable、layout坯钦、sting预皇、dimen、color和style 等等婉刀,另外項(xiàng)目中的 Activity 主題必須定義在 Common中吟温,方便和 BaseActivity 配合保持整個(gè)Android應(yīng)用的界面風(fēng)格統(tǒng)一。

下面是一份 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 Supportcompile"com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"compile"com.android.support:design:$rootProject.supportLibraryVersion"compile"com.android.support:percent:$rootProject.supportLibraryVersion"http://網(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"http://穩(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"http://routercompile"com.github.mzule.activityrouter:activityrouter:$rootProject.routerVersion"}

2)業(yè)務(wù)組件和Main組件

業(yè)務(wù)組件就是根據(jù)業(yè)務(wù)邏輯的不同拆分出來的組件突颊,業(yè)務(wù)組件的特征如下:

1鲁豪、業(yè)務(wù)組件中要有兩張AndroidManifest.xml,分別對(duì)應(yīng)組件開發(fā)模式和集成開發(fā)模式律秃,這兩張表的區(qū)別請(qǐng)查看?組件之間AndroidManifest合并問題?小節(jié)爬橡。

2、業(yè)務(wù)組件在集成模式下是不能有自己的Application的棒动,但在組件開發(fā)模式下又必須實(shí)現(xiàn)自己的Application并且要繼承自Common組件的BaseApplication糙申,并且這個(gè)Application不能被業(yè)務(wù)組件中的代碼引用,因?yàn)樗墓δ芫褪菫榱耸箻I(yè)務(wù)組件從BaseApplication中獲取的全局Context生效船惨,還有初始化數(shù)據(jù)之用柜裸。

3、業(yè)務(wù)組件有debug文件夾粱锐,這個(gè)文件夾在集成模式下會(huì)從業(yè)務(wù)組件的代碼中排除掉疙挺,所以debug文件夾中的類不能被業(yè)務(wù)組件強(qiáng)引用,例如組件模式下的 Application 就是置于這個(gè)文件夾中怜浅,還有組件模式下開發(fā)給目標(biāo) Activity 傳遞參數(shù)的用的 launch Activity 也應(yīng)該置于 debug 文件夾中衔统;

4、業(yè)務(wù)組件必須在自己的 Java文件夾中創(chuàng)建業(yè)務(wù)組件聲明類海雪,以使?app殼工程 中的 應(yīng)用Application能夠引用,實(shí)現(xiàn)組件跳轉(zhuǎn)舱殿,具體請(qǐng)查看?組件之間調(diào)用和通信?小節(jié)奥裸;

5、業(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伟恶,以及依賴其他用到的功能組件碴开。

下面是一份普通業(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組件除了有業(yè)務(wù)組件的普遍屬性外,還有一項(xiàng)重要功能

1即寒、Main組件集成模式下的AndroidManifest.xml是跟其他業(yè)務(wù)組件不一樣的橡淆,Main組件的表單中聲明了我們整個(gè)Android應(yīng)用的launch Activity,這就是Main組件的獨(dú)特之處母赵;所以我建議SplashActivity逸爵、登陸Activity以及主界面都應(yīng)屬于Main組件,也就是說Android應(yīng)用啟動(dòng)后要調(diào)用的頁面應(yīng)置于Main組件市咽。

5痊银、組件化項(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)建類型)的原因。

6刃永、工程的build.gradle和gradle.properties文件

1)組件化工程的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組件中的第三方開源庫的版本號(hào)抓督,最好也在這里定義這些開源庫的版本號(hào)燃少,然后在各個(gè)組件的build.gradle中引用Android工程根目錄下的build.gradle定義的版本號(hào),組件化工程的 build.gradle 文件代碼如下:

buildscript {? ? repositories {? ? ? ? jcenter()? ? ? ? mavenCentral()? ? }? ? dependencies {//classpath "com.android.tools.build:gradle:$localGradlePluginVersion"http://$localGradlePluginVersion是gradle.properties中的數(shù)據(jù)classpath"com.android.tools.build:gradle:$localGradlePluginVersion"}}allprojects {? ? repositories {? ? ? ? jcenter()? ? ? ? mavenCentral()//Add the JitPack repositorymaven { 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 =25minSdkVersion =16targetSdkVersion =25versionCode =1versionName ="1.0"javaVersion = JavaVersion.VERSION_1_8// App dependencies versionsupportLibraryVersion ="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"http://需檢查升級(jí)版本annotationProcessor ="1.1.7"routerVersion ="1.2.2"easyRecyclerVersion ="4.4.0"cookieVersion ="v1.0.1"toastyVersion ="1.1.3"}

2)組件化工程的gradle.properties文件

在組件化實(shí)施流程中我們了解到gradle.properties有兩個(gè)屬性對(duì)我們非常有用:

1阵具、在Android項(xiàng)目中的任何一個(gè)build.gradle文件中都可以把gradle.properties中的常量讀取出來,不管這個(gè)build.gradle是組件的還是整個(gè)項(xiàng)目工程的build.gradle涌穆;

2怔昨、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)改回來盒揉,并且不要把改動(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)從線上代碼隔離出來扁掸,保證線上代碼的 buildToolsVersion 和 Gradle Build Tools 版本號(hào)不會(huì)被人為改變翘县。

具體的實(shí)施流程大家可以查看我的這篇博文?AndroidStudio本地化配置gradle的buildToolsVersion和gradleBuildTools

7、組件化項(xiàng)目Router的其他方案-ARouter

在組件化項(xiàng)目中使用到的跨組件跳轉(zhuǎn)庫ActivityRouter可以使用阿里巴巴的開源路由項(xiàng)目:阿里巴巴ARouter谴分;

ActivityRouter和ARouter的接入組件化項(xiàng)目的方式是一樣的锈麸,ActivityRouter提供的功能目前ARouter也全部支持,但是ARouter還支持依賴注入解耦牺蹄,頁面忘伞、攔截器、服務(wù)等組件均會(huì)自動(dòng)注冊(cè)到框架沙兰。對(duì)于大家來說氓奈,沒有最好的只有最適合的,大家可以根據(jù)自己的項(xiàng)目選擇合適的Router鼎天。

下面將介紹ARouter的基礎(chǔ)使用方法舀奶,更多功能還需大家去Github自己學(xué)習(xí);

1斋射、首先 ARouter 這個(gè)框架是需要初始化SDK的育勺,所以你需要在“app殼工程”中的應(yīng)用Application中加入下面的代碼,注意:在 debug 模式下一定要 openDebug

if(BuildConfig.DEBUG) {//一定要在ARouter.init之前調(diào)用openDebugARouter.openDebug();? ? ? ? ? ? ARouter.openLog();? ? ? }? ? ? ARouter.init(this);

2罗岖、首先我們依然需要在 Common 組件中的 build.gradle 將ARouter 依賴進(jìn)來涧至,方便我們?cè)跇I(yè)務(wù)組件中調(diào)用:

dependencies {? ? compile fileTree(dir:'libs', include: ['*.jar'])//routercompile'com.alibaba:arouter-api:1.2.1.1'}

3、然后在每一個(gè)業(yè)務(wù)組件的 build.gradle 都引入ARouter 的 Annotation處理器桑包,代碼如下:

android {? ? defaultConfig {? ? ...? ? javaCompileOptions {? ? ? ? annotationProcessorOptions {? ? ? ? arguments = [ moduleName : project.getName() ]? ? ? ? }? ? }? ? }}dependencies {? ? compile fileTree(dir:'libs', include: ['*.jar'])? ? annotationProcessor'com.alibaba:arouter-compiler:1.0.3'}

4南蓬、由于ARouter支持自動(dòng)注冊(cè)到框架,所以我們不用像ActivityRouter那樣在各個(gè)組件中聲明組件哑了,當(dāng)然更不需要在Application中管理組件了赘方。?我們給 Girls組件 中的 GirlsActivity 添加注解:@Route(path = “/girls/list”),需要注意的是這里的路徑至少需要有兩級(jí)垒手,/xx/xx蒜焊,之所以這樣是因?yàn)?b>ARouter使用了路徑中第一段字符串(/*/)作為分組,比如像上面的”girls”科贬,而分組這個(gè)概念就有點(diǎn)類似于ActivityRouter中的組件聲明 @Module 泳梆,代碼如下:

@Route(path ="/girls/list")publicclassGirlsActivityextendsBaseActionBarActivity{privateGirlsView mView;privateGirlsContract.Presenter mPresenter;@OverrideprotectedintsetTitleId() {returnR.string.girls_activity_title;? ? }@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);? ? ? ? mView =newGirlsView(this);? ? ? ? setContentView(mView);? ? ? ? mPresenter =newGirlsPresenter(mView);? ? ? ? mPresenter.start();? ? }}

然后我們就可以在項(xiàng)目中的任何一個(gè)地方通過 URL地址 : /girls/list, 調(diào)用 GirlsActivity,方法如下:

ARouter.getInstance().build("/girls/list").navigation();

1

8榜掌、結(jié)束語

組件化相比于單一工程優(yōu)勢(shì)是顯而易見的:

組件模式下可以加快編譯速度优妙,提高開發(fā)效率;

自由選擇開發(fā)框架(MVC /MVP / MVVM /)憎账;

方便做單元測(cè)試套硼;

代碼架構(gòu)更加清晰,降低項(xiàng)目的維護(hù)難度胞皱;

適合于團(tuán)隊(duì)開發(fā)邪意;

最后貼出Android組件化Demo地址:Android組件化項(xiàng)目AndroidModulePattern

想要學(xué)習(xí)更多Android組件化知識(shí)九妈,請(qǐng)查看 :Android組件化之終極方案

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市雾鬼,隨后出現(xiàn)的幾起案子萌朱,更是在濱河造成了極大的恐慌,老刑警劉巖策菜,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晶疼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡又憨,警方通過查閱死者的電腦和手機(jī)翠霍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蠢莺,“玉大人寒匙,你說我怎么就攤上這事±嗣兀” “怎么了蒋情?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長耸携。 經(jīng)常有香客問我棵癣,道長,這世上最難降的妖魔是什么夺衍? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任狈谊,我火速辦了婚禮,結(jié)果婚禮上沟沙,老公的妹妹穿的比我還像新娘河劝。我一直安慰自己,他們只是感情好矛紫,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布赎瞎。 她就那樣靜靜地躺著,像睡著了一般颊咬。 火紅的嫁衣襯著肌膚如雪务甥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天喳篇,我揣著相機(jī)與錄音敞临,去河邊找鬼。 笑死麸澜,一個(gè)胖子當(dāng)著我的面吹牛挺尿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼编矾,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼熟史!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起窄俏,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤以故,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后裆操,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡炉媒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年踪区,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吊骤。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缎岗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出白粉,到底是詐尸還是另有隱情传泊,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布鸭巴,位于F島的核電站眷细,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鹃祖。R本人自食惡果不足惜溪椎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恬口。 院中可真熱鬧校读,春花似錦、人聲如沸祖能。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽养铸。三九已至雁芙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間揭厚,已是汗流浹背却特。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工筛圆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裂明,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像闽晦,于是被迫代替她去往敵國和親扳碍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容