1年半前我在博客中寫了一篇關(guān)于Android組件化的文章县匠,那個時候組件化在國內(nèi)還不是很流行邪码,我也是一個偶然的機(jī)會接觸到了這個技術(shù)宾尚,并且很神奇的在Github上找到了當(dāng)時還是同事的志濤同學(xué)的一個demo(感嘆一下他對gradle的理解之深孝情,雖然從這個demo中看不出來,但是在平時的交流過程中著恩,確實可以學(xué)習(xí)到很多)盖彭。
看完上面這個demo之后,又和提出這個思想的馮老師交流了一下页滚,感覺這個技術(shù)確實是對平時的開發(fā)召边,特別是多人開發(fā)有幫助的,于是在公司的項目中嘗試了一下(大概是16年的年底)裹驰,效果還不錯隧熙。
17年整一年幾乎都在做一個創(chuàng)新型App的研發(fā),在年中的時候把之前的組件化方案改造了一下并用在了新項目中幻林。
到現(xiàn)在接觸并使用組件化也有快2年的時間了贞盯,決定再回過頭來寫一篇關(guān)于組件化的文章音念,有不足之處還望大家能指出并交流。
為什么要寫
其實我原本是不想再寫和組件化相關(guān)的文章了躏敢,但是國內(nèi)最近關(guān)于Android組件化的文章如雨后春筍一樣的冒了出來闷愤,這樣的情景和16年全名hotpatch的“盛況”一模一樣,好像不搞這個東西就落伍了一樣件余。其中幾乎每一篇文章我都看過讥脐,說實話,四個字:千篇一律啼器。上來先講一下非組件化的壞處(編譯時間長旬渠,模塊混亂),然后講一下組件化的好處端壳,接著講一下路由的重要性告丢,最后再給你整一點(diǎn)gradle插件(基本都是通過transform去注入代碼)。這樣的文章在我看來有一些淺顯了损谦,對于不了解組件化的同學(xué)來說看一篇這樣的文章確實可以快速入門岖免,但是如果你真的要在實際項目中使用組件化,這樣的文章是遠(yuǎn)遠(yuǎn)不夠的照捡。
所以我的這篇文章不會聚焦于上面提到的組件化的優(yōu)點(diǎn)觅捆,路由以及gradle插件,更多的是一些經(jīng)驗麻敌,如果更好的進(jìn)行組件化。
對外友好
這一章節(jié)的主旨思想就是:模塊不僅要對自己負(fù)責(zé)掂摔,更好對其他模塊負(fù)責(zé)术羔。
舉個例子,我們現(xiàn)在有一個圖片模塊乙漓,還有一個編輯模塊级历,而編輯模塊需要用到圖片模塊中的一些方法(我更喜歡稱之為”服務(wù)”),這個時候我們需要建立圖片模塊和編輯模塊的依賴關(guān)系叭披,簡而言之寥殖,就是要讓編輯模塊能夠使用圖片模塊的服務(wù)。這在gradle這樣的構(gòu)建系統(tǒng)中很容易做到涩蜘,但是如果不假思索的去建立依賴關(guān)系嚼贡,會有一個很嚴(yán)重的問題,既圖片模塊會暴露不改暴露的類和方法同诫。簡單的依賴關(guān)系會讓整個圖片模塊暴露給編輯模塊粤策,這樣做的壞處就是一些內(nèi)部使用的util類編輯模塊也可以使用,從而帶來蝴蝶效應(yīng)误窖。比如圖片模塊中有一個util叫StringUtil叮盘,它將暴露給編輯模塊秩贰,如果編輯模塊的開發(fā)者在import的時候不留意,就會import到這個StringUtil柔吼,而圖片模塊的開發(fā)者認(rèn)為這個StringUtil是給自己用的毒费,可能會隨意修改里面的方法簽名或者返回值等,這是很危險的愈魏。
其實這樣的問題觅玻,如果你有web開發(fā)的經(jīng)驗,會很好解決蝌戒。在web開發(fā)中串塑,如果你的系統(tǒng)需要對別的系統(tǒng)暴露一些服務(wù),不論是rpc服務(wù)或者h(yuǎn)ttp服務(wù)北苟,都會在client模塊中新建一個接口桩匪,而在core模塊中實現(xiàn)它,最終對外暴露的只有client模塊友鼻,也就是只暴露了對應(yīng)的服務(wù)傻昙,而core模塊的一些util類是不會被引用到的。
這樣面向接口編程有很多的好處:第一就是解決了上面所說的過多暴露類和方法彩扔,第二就是對于調(diào)用者和被調(diào)用者來說都十分的清晰妆档,我所提供的服務(wù)就是接口中的方法,所有對外的增刪改都是基于接口的虫碉,在多人協(xié)作的情況下減小了溝通的成本贾惦。
基于以上的想法,在Android的模塊化協(xié)作中敦捧,我也建議大家使用這樣的方式去進(jìn)行模塊間的調(diào)用须板。
針對于上面的例子,我們對圖片模塊創(chuàng)建2個module:image和image-export兢卵。image-export就是前面提到的client习瑰,里面存放需要對外暴露的接口,可能還有一個bean等秽荤,image依賴image-export甜奄,實現(xiàn)其中的接口,并將一些只希望內(nèi)部使用的類放在其中窃款。對于這種的架構(gòu)模式课兄,我們還需要一個容器去注冊這些服務(wù),并讓其他模塊來調(diào)用它們晨继,我們會有一個componentManager類在做這樣的操作第喳,在App初始化的時候去收集所有需要注冊的類并通過反射初始化實例,運(yùn)行時按需調(diào)用踱稍。
持續(xù)集成友好
我看過的幾乎所有的組件化文章里提到的優(yōu)點(diǎn)都有一個:減少編譯時間曲饱。但是真的到項目中你就會發(fā)現(xiàn)悠抹,單獨(dú)編譯模塊的時候確實比較爽,但是當(dāng)集成之后扩淀,編譯時間會呈幾何形式形式的增長楔敌,原因就是在集成之后,每一個模塊都需要編譯驻谆,時間當(dāng)然會很長卵凑。而那些文章里多沒有提到這個問題,更沒有說如何解決胜臊。
就我而言勺卢,這樣的情況是不太能夠忍受的,既然是優(yōu)化象对,怎么能有這樣開倒車的事情發(fā)生黑忱。其實要解決這個問題也比較簡單,那就是在集成的時候通過aar的形式集成勒魔,當(dāng)我們在開發(fā)完成之后甫煞,可將對應(yīng)的模塊通過gradle上傳至倉庫中(對于本地開發(fā),上傳至本地倉庫冠绢;對于云端構(gòu)建抚吠,則上傳至自己的maven私服中)。這樣就可以做到按需更新弟胀,比如你有10個module楷力,這一次只在對其中2個更新,在構(gòu)建的時候只需要針對這2個module進(jìn)行aar發(fā)布就可以了孵户。
開發(fā)友好
對于組件化開發(fā)萧朝,我們可以做很多操作讓日常的開發(fā)變得簡單,下面我舉幾個例子:
AndroidStudio模板開發(fā)延届。組件化開發(fā)需要在gradle文件中做一些操作,包括新建一個module的時候贸诚,我們最好不要讓對應(yīng)的開發(fā)去copy已有的代碼方庭,太低效了。通過AndroidStudio模板的開發(fā)酱固,可以做到一鍵生成組件化所需要的gradle文件械念。
gradle文件優(yōu)化。對于上面的例子运悲,module和module-export里面肯定是比較多的重復(fù)代碼的龄减,我們可以抽取一個公共的gradle文件去做這樣的事,結(jié)合AndroidStudio模板班眯,將會讓文件變得十分簡潔易懂并且可復(fù)用希停。而對于模塊間的依賴烁巫,因為我們可能是lib依賴,也可能是aar依賴宠能,而在依賴中我們也可能通過api亚隙,implementation,compileOnly等方式去依賴违崇,我們也可以通過定義一個gradle方法去完成:
void aarOrLib(String moduleName, String scope = null){
?? if(useAAR() || isDebug()){
? ? ?? project.dependencies {
? ? ? ? ?? switch (scope){
? ? ? ? ? ? ?? case 'api':
? ? ? ? ? ? ? ? ?? api "xx.xx.xx:${moduleName}:${AAR_VERSION_NAME}"
? ? ? ? ? ? ? ? ?? break
? ? ? ? ? ? ?? case 'compileOnly':
? ? ? ? ? ? ? ? ?? compileOnly "xx.xx.xx:${moduleName}:${AAR_VERSION_NAME}"
? ? ? ? ? ? ? ? ?? break
? ? ? ? ? ? ?? default:
? ? ? ? ? ? ? ? ?? implementation "xx.xx.xx:${moduleName}:${AAR_VERSION_NAME}"
? ? ? ? ? ? ? ? ?? break
? ? ? ? ?? }
? ? ?? }
?? } else {
? ? ?? project.dependencies {
? ? ? ? ?? switch (scope){
? ? ? ? ? ? ?? case 'api':
? ? ? ? ? ? ? ? ?? api project(":${moduleName}")
? ? ? ? ? ? ? ? ?? break
? ? ? ? ? ? ?? case 'compileOnly':
? ? ? ? ? ? ? ? ?? compileOnly project(":${moduleName}")
? ? ? ? ? ? ? ? ?? break
? ? ? ? ? ? ?? default:
? ? ? ? ? ? ? ? ?? implementation project(":${moduleName}")
? ? ? ? ? ? ? ? ?? break
? ? ? ? ?? }
? ? ?? }
?? }
}
git flow開發(fā)阿弃。這個不多說了,結(jié)合feature多模塊平行開發(fā)羞延,也算是組件化的優(yōu)勢之一渣淳。
總結(jié)
這篇文章幾乎沒什么代碼,主要是對1年半來組件化開發(fā)的一個總結(jié)伴箩。
在看我來入愧,組件化的前提是所有基礎(chǔ)SDK下沉。這一點(diǎn)必須要滿足赛蔫,比如登錄組件砂客,如果這個組件沒有下沉而是存在于App中,在模塊化的時候就勢必會讓模塊反向依賴App呵恢,這是不合理的鞠值。
此外,基礎(chǔ)組件橫向劃分渗钉,業(yè)務(wù)縱向劃分彤恶,拒絕無意義的橫向,跨級以及反向依賴鳄橘,這是在組件化進(jìn)行中必須要遵守的原則声离。