成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Android組件化開發(fā)實(shí)踐和案例分享

zr_hebo / 3362人閱讀

摘要:主工程具有和組件進(jìn)行綁定和解綁的功能。如下圖組件化需要考慮問題考慮的問題分而治之,并行開發(fā),一切皆組件。引用阿里的框架,通過注解方式進(jìn)行頁(yè)面跳轉(zhuǎn)。

目錄介紹

1.為什么要組件化

1.1 為什么要組件化

1.2 現(xiàn)階段遇到的問題

2.組件化的概念

2.1 什么是組件化

2.2 區(qū)分模塊化與組件化

2.3 組件化優(yōu)勢(shì)好處

2.4 區(qū)分組件化和插件化

2.5 application和library

3.創(chuàng)建組件化框架

3.1 傳統(tǒng)APP架構(gòu)圖

3.2 組件化需要考慮問題

3.3 架構(gòu)設(shè)計(jì)圖

3.4 組件通信是通過路由轉(zhuǎn)發(fā)

3.5 解決考慮問題

3.6 業(yè)務(wù)組件的生命周期

3.7 Fragment通信難點(diǎn)

4.實(shí)際開發(fā)案例

4.1 組件化實(shí)踐的開源項(xiàng)目

4.1 如何創(chuàng)建模塊

4.2 如何建立依賴

4.3 如何統(tǒng)一配置文件

4.4 組件化的基礎(chǔ)庫(kù)

4.5 組件模式和集成模式如何切換

4.6 組件化解決重復(fù)依賴

4.7 組件化注意要點(diǎn)

4.8 組件化時(shí)資源名沖突

4.9 組件化開發(fā)遇到問題

5.組件間通信

5.1 選擇那個(gè)開源路由庫(kù)

5.2 阿里Arouter基礎(chǔ)原理

5.3 使用Arouter注意事項(xiàng)

6.關(guān)于其他

6.1 參考博客鏈接

6.2 我的博客介紹

6.3 開源項(xiàng)目地址

1.為什么要組件化 1.1 為什么要組件化

APP迭代維護(hù)成本增高

投資界,新芽,項(xiàng)目工廠等APP自身在飛速發(fā)展,版本不斷迭代,新功能不斷增加,業(yè)務(wù)模塊數(shù)量不斷增加,業(yè)務(wù)上的處理邏輯越變?cè)綇?fù)雜,同時(shí)每個(gè)模塊代碼也變得越來越多,這就引發(fā)一個(gè)問題,所維護(hù)的代碼成本越來越高,稍微一改動(dòng)可能就牽一發(fā)而動(dòng)全身,改個(gè)小的功能點(diǎn)就需要回歸整個(gè)APP測(cè)試,這就對(duì)開發(fā)和維護(hù)帶來很大的挑戰(zhàn)。

多人組合需要組件化

APP 架構(gòu)方式是單一工程模式,業(yè)務(wù)規(guī)模擴(kuò)大,隨之帶來的是團(tuán)隊(duì)規(guī)模擴(kuò)大,那就涉及到多人協(xié)作問題,每個(gè)移動(dòng)端軟件開發(fā)人員勢(shì)必要熟悉如此之多代碼,如果不按照一定的模塊組件機(jī)制去劃分,將很難進(jìn)行多人協(xié)作開發(fā),隨著單一項(xiàng)目變大,而且Andorid項(xiàng)目在編譯代碼方面就會(huì)變得非??D,在單一工程代碼耦合嚴(yán)重,每修改一處代碼后都需要重新編譯打包測(cè)試,導(dǎo)致非常耗時(shí)。

1.2 現(xiàn)階段遇到的問題

結(jié)合投資界,新芽客戶端分析

代碼量膨脹,不利于維護(hù),不利于新功能的開發(fā)。項(xiàng)目工程構(gòu)建速度慢,在一些電腦上寫兩句代碼,重新編譯整個(gè)項(xiàng)目,測(cè)試的話編譯速度起碼 10-20 分鐘,有的甚至更長(zhǎng)。

不同模塊之間代碼耦合嚴(yán)重,有時(shí)候修改一處代碼而牽動(dòng)許多模塊。每個(gè)模塊之間都有引用第三方庫(kù),但有些第三方庫(kù)版本不一致,導(dǎo)致打包APP時(shí)候代碼冗余,容易引起版本沖突。

現(xiàn)有項(xiàng)目基于以前其他人項(xiàng)目基礎(chǔ)上開發(fā),經(jīng)手的人次過多,存在著不同的代碼風(fēng)格,項(xiàng)目中代碼規(guī)范亂,類似的功能寫法卻不一樣,導(dǎo)致不統(tǒng)一。

2.組件化的概念 2.1 什么是組件化

什么是組件化呢?

組件(Component)是對(duì)數(shù)據(jù)和方法的簡(jiǎn)單封裝,功能單一,高內(nèi)聚,并且是業(yè)務(wù)能劃分的最小粒度。

組件化是基于組件可重用的目的上,將一個(gè)大的軟件系統(tǒng)按照分離關(guān)注點(diǎn)的形式,拆分成多個(gè)獨(dú)立的組件,使得整個(gè)軟件系統(tǒng)也做到電路板一樣,是單個(gè)或多個(gè)組件元件組裝起來,哪個(gè)組件壞了,整個(gè)系統(tǒng)可繼續(xù)運(yùn)行,而不出現(xiàn)崩潰或不正?,F(xiàn)象,做到更少的耦合和更高的內(nèi)聚。

2.2 區(qū)分模塊化與組件化

模塊化

模塊化就是將一個(gè)程序按照其功能做拆分,分成相互獨(dú)立的模塊,以便于每個(gè)模塊只包含與其功能相關(guān)的內(nèi)容,模塊我們相對(duì)熟悉,比如登錄功能可以是一個(gè)模塊,搜索功能可以是一個(gè)模塊等等。

組件化

組件化就是更關(guān)注可復(fù)用性,更注重關(guān)注點(diǎn)分離,如果從集合角度來看的話,可以說往往一個(gè)模塊包含了一個(gè)或多個(gè)組件,或者說模塊是一個(gè)容器,由組件組裝而成。簡(jiǎn)單來說,組件化相比模塊化粒度更小,兩者的本質(zhì)思想都是一致的,都是把大往小的方向拆分,都是為了復(fù)用和解耦,只不過模塊化更加側(cè)重于業(yè)務(wù)功能的劃分,偏向于復(fù)用,組件化更加側(cè)重于單一功能的內(nèi)聚,偏向于解耦。

2.3 組件化優(yōu)勢(shì)好處

簡(jiǎn)單來說就是提高工作效率,解放生產(chǎn)力,好處如下:

1.提高編譯速度,從而提高并行開發(fā)效率。

問題:那么如何提高編譯速度的呢?組件化框架可以使模塊多帶帶編譯調(diào)試,可以有效地減少編譯的時(shí)間。

2.穩(wěn)定的公共模塊采用依賴庫(kù)方式

提供給各個(gè)業(yè)務(wù)線使用,減少重復(fù)開發(fā)和維護(hù)工作量。代碼簡(jiǎn)潔,冗余量少,維護(hù)方便,易擴(kuò)展新功能。

3.每個(gè)組件有自己獨(dú)立的版本,可以獨(dú)立編譯、測(cè)試、打包和部署。

針對(duì)開發(fā)程序員多的公司,組件化很有必要,每個(gè)人負(fù)責(zé)自己的模塊,可以較少提交代碼沖突。

為新業(yè)務(wù)隨時(shí)集成提供了基礎(chǔ),所有業(yè)務(wù)可上可下,靈活多變。

各業(yè)務(wù)線研發(fā)可以互不干擾、提升協(xié)作效率,并控制產(chǎn)品質(zhì)量。

4.避免模塊之間的交叉依賴,做到低耦合、高內(nèi)聚。

5.引用的第三方庫(kù)代碼統(tǒng)一管理,避免版本統(tǒng)一,減少引入冗余庫(kù)。

這個(gè)可以創(chuàng)建一個(gè)公共的gradle管理的文件,比如一個(gè)項(xiàng)目有十幾個(gè)組件,想要改下某個(gè)庫(kù)或者版本號(hào),總不至于一個(gè)個(gè)修改吧。這個(gè)時(shí)候提取公共十分有必要

6.定制項(xiàng)目可按需加載,組件之間可以靈活組建,快速生成不同類型的定制產(chǎn)品。

2.4 區(qū)分組件化和插件化

組件化和插件化的區(qū)別

組件化不是插件化,插件化是在【運(yùn)行時(shí)】,而組件化是在【編譯時(shí)】。換句話說,插件化是基于多APK的,而組件化本質(zhì)上還是只有一個(gè) APK。

組件化和插件化的最大區(qū)別(應(yīng)該也是唯一區(qū)別)就是組件化在運(yùn)行時(shí)不具備動(dòng)態(tài)添加和修改組件的功能,但是插件化是可以的。

組件化的目標(biāo)

組件化的目標(biāo)之一就是降低整體工程(app)與組件的依賴關(guān)系,缺少任何一個(gè)組件都是可以存在并正常運(yùn)行的。app主工程具有和組件進(jìn)行綁定和解綁的功能。

2.5 application和library

在studio中,對(duì)兩種module進(jìn)行區(qū)分,如下所示

一種是基礎(chǔ)庫(kù)library,比如常見第三方庫(kù)都是lib,這些代碼被其他組件直接引用。

另一種是application,也稱之為Component,這種module是一個(gè)完整的功能模塊。比如分享module就是一個(gè)Component。

為了方便,統(tǒng)一把library稱之為依賴庫(kù),而把Component稱之為組件,下面所講的組件化也主要是針對(duì)Component這種類型。

在項(xiàng)目的build.gradle文件中

//控制組件模式和集成模式
if (rootProject.ext.isDouBanApplication) {
    //是Component,可以獨(dú)立運(yùn)行
    apply plugin: "com.android.application"
} else {
    //是lib,被依賴
    apply plugin: "com.android.library"
}

3.創(chuàng)建組件化框架 3.1 傳統(tǒng)APP架構(gòu)圖

傳統(tǒng)APP架構(gòu)圖

如圖所示,從網(wǎng)上摘來的……

存在的問題

普遍使用的 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)系,總的來說就是:你中有我,我中有你,相互依賴,無(wú)法分離。如下圖:

3.2 組件化需要考慮問題

考慮的問題

分而治之,并行開發(fā),一切皆組件。要實(shí)現(xiàn)組件化,無(wú)論采用什么樣的技術(shù)方式,需要考慮以下七個(gè)方面問題:

代碼解耦。

如何將一個(gè)龐大的工程分成有機(jī)的整體?這個(gè)需要一步步來了!

對(duì)已存在的項(xiàng)目進(jìn)行模塊拆分,模塊分為兩種類型,一種是功能組件模塊,封裝一些公共的方法服務(wù)等,作為依賴庫(kù)對(duì)外提供;另一種是業(yè)務(wù)組件模塊,專門處理業(yè)務(wù)邏輯等功能,這些業(yè)務(wù)組件模塊最終負(fù)責(zé)組裝APP。

組件多帶帶運(yùn)行。

因?yàn)槊總€(gè)組件都是高度內(nèi)聚的,是一個(gè)完整的整體,如何讓其多帶帶運(yùn)行和調(diào)試?

通過 Gradle腳本配置方式,進(jìn)行不同環(huán)境切換,我自己操作是添加一個(gè)boolean值的開關(guān)。比如只需要把 Apply plugin: "com.android.library" 切換成Apply plugin: "com.android.application" 就可以獨(dú)立運(yùn)行呢!

需要注意:當(dāng)切換到application獨(dú)立運(yùn)行時(shí),需要在AndroidManifest清單文件上進(jìn)行設(shè)置,因?yàn)橐粋€(gè)多帶帶調(diào)試需要有一個(gè)入口的Activity。

組件間通信。

由于每個(gè)組件具體實(shí)現(xiàn)細(xì)節(jié)都互相不了解,但每個(gè)組件都需要給其他調(diào)用方提供服務(wù),那么主項(xiàng)目與組件、組件與組件之間如何通信就變成關(guān)鍵?

這個(gè)我是直接用阿里開源的路由框架,當(dāng)然你可以根據(jù)需要選擇其他大廠的開源路由庫(kù)。引用阿里的ARouter框架,通過注解方式進(jìn)行頁(yè)面跳轉(zhuǎn)。

組件生命周期。

這里的生命周期指的是組件在應(yīng)用中存在的時(shí)間,組件是否可以做到按需、動(dòng)態(tài)使用、因此就會(huì)涉及到組件加載、卸載等管理問題。

集成調(diào)試。

在開發(fā)階段如何做到按需編譯組件?一次調(diào)試中可能有一兩個(gè)組件參與集成,這樣編譯時(shí)間就會(huì)大大降低,提高開發(fā)效率。

代碼隔離。

組件之間的交互如果還是直接引用的話,那么組件之間根本沒有做到解耦,如何從根本上避免組件之間的直接引用?目前做法是主項(xiàng)目和業(yè)務(wù)組件都會(huì)依賴公共基礎(chǔ)組件庫(kù),業(yè)務(wù)組件通過路由服務(wù)依賴庫(kù)按需進(jìn)行查找,用于不同組件之間的通信。

告別結(jié)構(gòu)臃腫,讓各個(gè)業(yè)務(wù)變得相對(duì)獨(dú)立,業(yè)務(wù)組件在組件模式下可以獨(dú)立開發(fā),而在集成模式下又可以變?yōu)锳AR包集成到“APP殼工程”中,組成一個(gè)完整功能的 APP。

3.3 架構(gòu)設(shè)計(jì)圖

組件化架構(gòu)圖

業(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í)間。

3.4 組件通信是通過路由轉(zhuǎn)發(fā)

傳統(tǒng)以前工程下模塊

記得剛開始進(jìn)入Android開發(fā)工作時(shí),只有一個(gè)app主工程,后期幾乎所有的需求都寫在這個(gè)app主工程里面。只有簡(jiǎn)單的以業(yè)務(wù)邏輯劃分的文件夾,并且業(yè)務(wù)之間也是直接相互調(diào)用、高度耦合在一起的。

導(dǎo)致后期改項(xiàng)目為組件化的時(shí)候十分痛苦,不同模塊之間的業(yè)務(wù)邏輯實(shí)在關(guān)聯(lián)太多,但還是沒辦法,于是目錄4步驟一步步實(shí)踐。終極目標(biāo)是,告別結(jié)構(gòu)臃腫,讓各個(gè)業(yè)務(wù)變得相對(duì)獨(dú)立,業(yè)務(wù)組件在組件模式下可以獨(dú)立開發(fā)。

組件化模式下如何通信

這是組件化工程模型下的業(yè)務(wù)關(guān)系,業(yè)務(wù)之間將不再直接引用和依賴,而是通過“路由”這樣一個(gè)中轉(zhuǎn)站間接產(chǎn)生聯(lián)系。在這個(gè)開源項(xiàng)目中,我使用的阿里開源的路由框架。關(guān)于Arouter基礎(chǔ)使用和代碼分析,可以看我這篇博客:Arouter使用與代碼解析

3.6 業(yè)務(wù)組件的生命周期

按照理想狀態(tài)的來看待的話

各個(gè)業(yè)務(wù)組件之間沒有任何依賴關(guān)系,這時(shí)我們可以把每個(gè)獨(dú)立的業(yè)務(wù)組件看成一個(gè)可運(yùn)行的app,所以業(yè)務(wù)組件的生命周期和應(yīng)與獨(dú)立的app保持一致。

3.7 Fragment通信難點(diǎn)

在網(wǎng)上看到很多博客說,如何拆分組件,按模塊拆分,或者按照功能拆分。但很少有提到fragment在拆分組件時(shí)的疑問,這個(gè)讓我很奇怪。

先來說一個(gè)業(yè)務(wù)需求,比如一個(gè)購(gòu)物商城app,有4個(gè)模塊,做法一般是一個(gè)activity+4個(gè)fragment,這個(gè)大家都很熟悉,這四個(gè)模塊分別是:首頁(yè),發(fā)現(xiàn),購(gòu)物車,我的。然后這幾個(gè)頁(yè)面是用fragment寫的,共用一個(gè)宿主activity,那么在做組件化的時(shí)候,我想把它按照業(yè)務(wù)拆分成首頁(yè),發(fā)現(xiàn),購(gòu)物車和我的四個(gè)獨(dú)立的業(yè)務(wù)模塊。

遇到疑問:

如果是拆分成四個(gè)獨(dú)立的業(yè)務(wù)模塊,那么對(duì)應(yīng)的fragment肯定要放到對(duì)應(yīng)的組件中,那么這樣操作,當(dāng)主工程與該業(yè)務(wù)組件解綁的情況下,如何拿到fragment和傳遞參數(shù)進(jìn)行通信。

Fragment 中 開啟Activity帶requestCode,開啟的Activity關(guān)閉后,不會(huì)回調(diào)Fragment中的onActivityResult。只會(huì)調(diào)用Fragment 所在Activity的onActivityResult。

多fragment單activity攔截器不管用,難道只能用于攔截activity的跳轉(zhuǎn)?那如果是要實(shí)現(xiàn)登錄攔截的話,那不是只能在PathReplaceService中進(jìn)行了?

網(wǎng)絡(luò)解決辦法

第一個(gè)疑問:由于我使用阿里路由,所以我看到zhi1ong大佬說:用Router跳轉(zhuǎn)到這個(gè)Activity,然后帶一個(gè)參數(shù)進(jìn)去,比方說tab=2,然后自己在onCreate里面自行切換。但后來嘗試,還是想問問廣大程序員有沒有更好的辦法。

第二個(gè)疑問:還是zhi1ong大佬說,通過廣播,或者在Activity中轉(zhuǎn)發(fā)這個(gè)事件,比方說讓Fragment統(tǒng)一依賴一個(gè)接口,然后在Activity中轉(zhuǎn)發(fā)。

4.實(shí)際開發(fā)案例 4.1 組件化實(shí)踐的開源項(xiàng)目

關(guān)于組件化開發(fā)一點(diǎn)感想

關(guān)于網(wǎng)上有許多關(guān)于組件化的博客,講解了什么是組件化,為何要組件化,以及組件化的好處。大多數(shù)文章提供了組件化的思路,給我著手組件化開發(fā)提供了大量的便利。感謝前輩大神的分享!雖然有一些收獲,但是很少有文章能夠給出一個(gè)整體且有效的方案,或者一個(gè)具體的Demo。

但是畢竟看博客也是為了實(shí)踐做準(zhǔn)備,當(dāng)著手將之前的開源案例改版成組件化案例時(shí),出現(xiàn)了大量的問題,也解決了一些問題。主要是學(xué)些了組件化開發(fā)流程。

大多數(shù)公司慢慢著手組件化開發(fā),在小公司,有的人由于之前沒有做過組件化開發(fā),嘗試組件化也是挺好的;在大公司,有的人一去只是負(fù)責(zé)某個(gè)模塊,可能剛開始組件化已經(jīng)有人弄好了,那學(xué)習(xí)實(shí)踐組件化那更快一些。業(yè)余實(shí)踐,改版之前開源項(xiàng)目,寫了這篇博客,耗費(fèi)我不少時(shí)間,要是對(duì)你有些幫助,那我就很開心呢。由于我也是個(gè)小人物,所以寫的不好,勿噴,歡迎提出建議!

關(guān)于組件化開源項(xiàng)目

項(xiàng)目整體架構(gòu)模式采用:組件化+MVP+Rx+Retrofit+design+Dagger2+VLayout+X5

包含的模塊:wanAndroid【kotlin】+干貨集中營(yíng)+知乎日?qǐng)?bào)+番茄Todo+精選新聞+豆瓣音樂電影小說+小說讀書+簡(jiǎn)易記事本+搞笑視頻+經(jīng)典游戲+其他更多等等

此項(xiàng)目屬于業(yè)余時(shí)間練手的項(xiàng)目,接口數(shù)據(jù)來源均來自網(wǎng)絡(luò),如果存在侵權(quán)情況,請(qǐng)第一時(shí)間告知。本項(xiàng)目?jī)H做學(xué)習(xí)交流使用,API數(shù)據(jù)內(nèi)容所有權(quán)歸原作公司所有,請(qǐng)勿用于其他用途。

關(guān)于開源組件化的項(xiàng)目地址:https://github.com/yangchong2...

4.1 如何創(chuàng)建模塊

根據(jù)3.3 架構(gòu)設(shè)計(jì)圖可以知道

主工程:

除了一些全局配置和主 Activity 之外,不包含任何業(yè)務(wù)代碼。有的也叫做空殼app,主要是依賴業(yè)務(wù)組件進(jìn)行運(yùn)行。

業(yè)務(wù)組件:

最上層的業(yè)務(wù),每個(gè)組件表示一條完整的業(yè)務(wù)線,彼此之間互相獨(dú)立。原則上來說:各個(gè)業(yè)務(wù)組件之間不能有直接依賴!所有的業(yè)務(wù)組件均需要可以做到獨(dú)立運(yùn)行的效果。對(duì)于測(cè)試的時(shí)候,需要依賴多個(gè)業(yè)務(wù)組件的功能進(jìn)行集成測(cè)試的時(shí)候??梢允褂胊pp殼進(jìn)行多組件依賴管理運(yùn)行。

該案例中分為:干活集中營(yíng),玩Android,知乎日?qǐng)?bào),微信新聞,頭條新聞,搞笑視頻,百度音樂,我的記事本,豆瓣音樂讀書電影,游戲組件等等。

功能組件:

該案例中分為,分享組件,評(píng)論反饋組件,支付組件,畫廊組件等等。同時(shí)注意,可能會(huì)涉及多個(gè)業(yè)務(wù)組件對(duì)某個(gè)功能組件進(jìn)行依賴!

基礎(chǔ)組件:

支撐上層業(yè)務(wù)組件運(yùn)行的基礎(chǔ)業(yè)務(wù)服務(wù)。此部分組件為上層業(yè)務(wù)組件提供基本的功能支持。

該案例中:在基礎(chǔ)組件庫(kù)中主要有,網(wǎng)絡(luò)請(qǐng)求,圖片加載,通信機(jī)制,工具類,分享功能,支付功能等等。當(dāng)然,我把一些公共第三方庫(kù)放到了這個(gè)基礎(chǔ)組件中!

4.2 如何建立依賴

關(guān)于工程中組件依賴結(jié)構(gòu)圖如下所示

業(yè)務(wù)模塊下完整配置代碼

//控制組件模式和集成模式
if (rootProject.ext.isGankApplication) {
    apply plugin: "com.android.application"
} else {
    apply plugin: "com.android.library"
}

android {
    compileSdkVersion rootProject.ext.android["compileSdkVersion"]
    buildToolsVersion rootProject.ext.android["buildToolsVersion"]


    defaultConfig {
        minSdkVersion rootProject.ext.android["minSdkVersion"]
        targetSdkVersion rootProject.ext.android["targetSdkVersion"]
        versionCode rootProject.ext.android["versionCode"]
        versionName rootProject.ext.android["versionName"]

        if (rootProject.ext.isGankApplication){
            //組件模式下設(shè)置applicationId
            applicationId "com.ycbjie.gank"
        }
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        }
    }

    //jdk1.8
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    sourceSets {
        main {
            if (rootProject.ext.isGankApplication) {
                manifest.srcFile "src/main/module/AndroidManifest.xml"
            } else {
                manifest.srcFile "src/main/AndroidManifest.xml"
            }
            jniLibs.srcDirs = ["libs"]
        }
    }

}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation project(":library")
    annotationProcessor rootProject.ext.dependencies["router-compiler"]
}

4.3 如何統(tǒng)一配置文件

由于組件化實(shí)踐中模塊比較多,因此配置gradle,添加依賴庫(kù)時(shí),需要考慮簡(jiǎn)化工作。那么究竟如何做呢?

第一步,首先在項(xiàng)目根目錄下創(chuàng)建一個(gè)yc.gradle文件。實(shí)際開發(fā)中只需要更改該文件中版本信息即可。

我在網(wǎng)上看到的絕大多數(shù)案例,都是通過一個(gè)開關(guān)控件組件模式和集成模式的切換,但是這里我配置了多個(gè)組件的開關(guān),分別控制對(duì)應(yīng)的組件切換狀態(tài)。

ext {

    isApplication = false  //false:作為L(zhǎng)ib組件存在, true:作為application存在
    isAndroidApplication = false  //玩Android模塊開關(guān),false:作為L(zhǎng)ib組件存在, true:作為application存在
    isLoveApplication = false  //愛意表達(dá)模塊開關(guān),false:作為L(zhǎng)ib組件存在, true:作為application存在
    isVideoApplication = false  //視頻模塊開關(guān),false:作為L(zhǎng)ib組件存在, true:作為application存在
    isNoteApplication = false  //記事本模塊開關(guān),false:作為L(zhǎng)ib組件存在, true:作為application存在
    isBookApplication = false  //book模塊開關(guān),false:作為L(zhǎng)ib組件存在, true:作為application存在
    isDouBanApplication = false  //豆瓣模塊開關(guān),false:作為L(zhǎng)ib組件存在, true:作為application存在
    isGankApplication = false  //干貨模塊開關(guān),false:作為L(zhǎng)ib組件存在, true:作為application存在
    isMusicApplication = false  //音樂模塊開關(guān),false:作為L(zhǎng)ib組件存在, true:作為application存在
    isNewsApplication = false  //新聞模塊開關(guān),false:作為L(zhǎng)ib組件存在, true:作為application存在
    isToDoApplication = false  //todo模塊開關(guān),false:作為L(zhǎng)ib組件存在, true:作為application存在
    isZhiHuApplication = false  //知乎模塊開關(guān),false:作為L(zhǎng)ib組件存在, true:作為application存在
    isOtherApplication = false  //其他模塊開關(guān),false:作為L(zhǎng)ib組件存在, true:作為application存在
    
    android = [
               compileSdkVersion       : 28,
               buildToolsVersion       : "28.0.3",
               minSdkVersion           : 17,
               targetSdkVersion        : 28,
               versionCode             : 22,
               versionName             : "1.8.2"    //必須是int或者float,否則影響線上升級(jí)
    ]

    version = [
               androidSupportSdkVersion: "28.0.0",
               retrofitSdkVersion      : "2.4.0",
               glideSdkVersion         : "4.8.0",
               canarySdkVersion        : "1.5.4",
               constraintVersion       : "1.0.2"
    ]

    dependencies = [
                //support
                "appcompat-v7"             : "com.android.support:appcompat-v7:${version["androidSupportSdkVersion"]}",
                "multidex"                 : "com.android.support:multidex:1.0.1",
                //network
                "retrofit"                 : "com.squareup.retrofit2:retrofit:${version["retrofitSdkVersion"]}",
                "retrofit-converter-gson"  : "com.squareup.retrofit2:converter-gson:${version["retrofitSdkVersion"]}",
                "retrofit-adapter-rxjava"  : "com.squareup.retrofit2:adapter-rxjava2:${version["retrofitSdkVersion"]}",
                //這里省略一部分代碼
        ]
}

第二步,然后在項(xiàng)目中的lib【注意這里是放到基礎(chǔ)組件庫(kù)的build.gradle】中添加代碼,如下所示

apply plugin: "com.android.library"

android {
    compileSdkVersion rootProject.ext.android["compileSdkVersion"]
    buildToolsVersion rootProject.ext.android["buildToolsVersion"]


    defaultConfig {
        minSdkVersion rootProject.ext.android["minSdkVersion"]
        targetSdkVersion rootProject.ext.android["targetSdkVersion"]
        versionCode rootProject.ext.android["versionCode"]
        versionName rootProject.ext.android["versionName"]
    }
}


dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    api rootProject.ext.dependencies["appcompat-v7"]
    api rootProject.ext.dependencies["design"]
    api rootProject.ext.dependencies["palette"]
    api rootProject.ext.dependencies["glide"]
    api (rootProject.ext.dependencies["glide-transformations"]){
        exclude module: "glide"
    }
    annotationProcessor rootProject.ext.dependencies["glide-compiler"]
    api files("libs/tbs_sdk_thirdapp_v3.2.0.jar")
    api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    
    //省略部分代碼
}

第三步,在其他model中添加依賴

implementation project(":library")即可。

4.4 組件化的基礎(chǔ)庫(kù)

基礎(chǔ)庫(kù)組件封裝

基礎(chǔ)庫(kù)組件封裝庫(kù)中主要包括開發(fā)常用的一些框架。可以直接看我的項(xiàng)目更加直觀!

1、網(wǎng)絡(luò)請(qǐng)求(采用Retrofit+RxJava框架),攔截器

2、圖片加載(策略模式,Glide與Picasso之間可以切換)

3、通信機(jī)制(RxBus),路由ARouter簡(jiǎn)單封裝工具類(不同model之間通信)

4、mvp框架,常用的base類,比如BaseActivity,BaseFragment等等

5、通用的工具類,比如切割圓角,動(dòng)畫工具類等等

6、自定義view(包括對(duì)話框,ToolBar布局,圓形圖片等view的自定義)

7、共有的shape,drawable,layout,color等資源文件

8、全局初始化異步線程池封裝庫(kù),各個(gè)組件均可以用到

組件初始化

比如,你將該案例中的新聞組件切換成獨(dú)立運(yùn)行的app,那么由于新聞跳轉(zhuǎn)詳情頁(yè)需要使用到x5的WebView,因此需要對(duì)它進(jìn)行初始化。最剛開始做法是,為每一個(gè)可以切換成app的組件配置一個(gè)獨(dú)立的application,然后初始化一些該組件需要初始化的任務(wù)。但是這么做,有一點(diǎn)不好,不是很方便管理。后來看了知乎組件化實(shí)踐方案后,該方案提出,開發(fā)了一套多線程初始化框架,每個(gè)組件只要新建若干個(gè)啟動(dòng) Task 類,并在 Task 中聲明依賴關(guān)系。但是具體怎么用到代碼中后期有待實(shí)現(xiàn)!

如何簡(jiǎn)化不熟悉組件化的人快速適應(yīng)組件獨(dú)立運(yùn)行

設(shè)置多個(gè)組件開關(guān),需要切換那個(gè)組件就改那個(gè)。如果設(shè)置一個(gè)開關(guān),要么把所有組件切成集成模式,要么把所有組件切成組件模式,有點(diǎn)容易出問題。更多可以往下看!

嚴(yán)格限制公共基礎(chǔ)組件的增長(zhǎng)

隨著開發(fā)不斷進(jìn)行,要注意不要往基礎(chǔ)公共組件加入太多內(nèi)容。而是應(yīng)該減小體積!倘若是基礎(chǔ)組件過于龐大,那么運(yùn)行組件也是比較緩慢的!

4.5 組件模式和集成模式如何切換

在玩Android組件下的build.gradle文件,其他組件類似。

通過一個(gè)開關(guān)來控制這個(gè)狀態(tài)的切換,module如果是一個(gè)庫(kù),會(huì)使用com.android.library插件;如果是一個(gè)應(yīng)用,則使用com.android.application插件

//控制組件模式和集成模式
if (rootProject.ext.isAndroidApplication) {
    apply plugin: "com.android.application"
} else {
    apply plugin: "com.android.library"
}

集成模式如下所示

首先需要在yc.gradle文件中設(shè)置 isApplication=false。Sync下后,發(fā)現(xiàn)該組件是library

ext {
    isAndroidApplication = false  //false:作為L(zhǎng)ib組件存在, true:作為application存在

組件模式如下所示

首先需要在yc.gradle文件中設(shè)置 isApplication=true。Sync下后,發(fā)現(xiàn)該組件是application,即可針對(duì)模塊進(jìn)行運(yùn)行

ext {
    isAndroidApplication = true  //false:作為L(zhǎng)ib組件存在, true:作為application存在

需要注意的地方,這個(gè)很重要

首先看看網(wǎng)上絕大多數(shù)的作法,非常感謝這些大神的無(wú)私奉獻(xiàn)!但是我覺得多個(gè)組件用一個(gè)開關(guān)控制也可以,但是sourceSets里面切換成組件app時(shí),可以直接不用下面這么麻煩,可以復(fù)用java和res文件。

接下來看看我的做法:

下面這個(gè)配置十分重要。也就是說當(dāng)該玩Android組件從library切換到application時(shí),由于可以作為獨(dú)立app運(yùn)行,所以序意設(shè)置applicationId,并且配置清單文件,如下所示!

在 library 和 application 之間切換,manifest文件也需要提供兩套

android {
    defaultConfig {
        if (rootProject.ext.isAndroidApplication){
            //組件模式下設(shè)置applicationId
            applicationId "com.ycbjie.android"
        }
    }
    sourceSets {
        main {
            if (rootProject.ext.isAndroidApplication) {
                manifest.srcFile "src/main/module/AndroidManifest.xml"
            } else {
                manifest.srcFile "src/main/AndroidManifest.xml"
            }
            jniLibs.srcDirs = ["libs"]
        }
    }
}

具體在項(xiàng)目中如下所示

4.6 組件化解決重復(fù)依賴

重復(fù)依賴問題說明

重復(fù)依賴問題其實(shí)在開發(fā)中經(jīng)常會(huì)遇到,比如項(xiàng)目 implementation 了一個(gè)A,然后在這個(gè)庫(kù)里面又 implementation 了一個(gè)B,然后你的工程中又 implementation 了一個(gè)同樣的B,就依賴了兩次。

默認(rèn)情況下,如果是 aar 依賴,gradle 會(huì)自動(dòng)幫我們找出新版本的庫(kù)而拋棄舊版本的重復(fù)依賴。但是如果使用的是project依賴,gradle并不會(huì)去去重,最后打包就會(huì)出現(xiàn)代碼中有重復(fù)的類了。

解決辦法,舉個(gè)例子

api(rootProject.ext.dependencies["logger"]) { 
    exclude module: "support-v4"http://根據(jù)組件名排除 
    exclude group: "android.support.v4"http://根據(jù)包名排除 
}

4.7 組件化注意要點(diǎn)

業(yè)務(wù)組件之間聯(lián)動(dòng)導(dǎo)致耦合嚴(yán)重

比如,實(shí)際開發(fā)中,購(gòu)物車和首頁(yè)商品分別是兩個(gè)組件。但是遇到產(chǎn)品需求,比如過節(jié)做個(gè)活動(dòng),發(fā)個(gè)購(gòu)物券之類的需求,由于購(gòu)物車和商品詳情頁(yè)都有活動(dòng),因此會(huì)造成組件經(jīng)常會(huì)發(fā)生聯(lián)動(dòng)。倘若前期準(zhǔn)備不足,隨著時(shí)間的推移,各個(gè)業(yè)務(wù)線的代碼邊界會(huì)像組件化之前的主工程一樣逐漸劣化,耦合會(huì)越來越嚴(yán)重。

第一種解決方式:使用 sourceSets 的方式將不同的業(yè)務(wù)代碼放到不同的文件夾,但是 sourceSets 的問題在于,它并不能限制各個(gè) sourceSet 之間互相引用,所以這種方式并不太友好!

第二種解決方式:抽取需求為工具類,通過不同組件傳值而達(dá)到調(diào)用關(guān)系,這樣只需要改工具類即可改需求。但是這種只是符合需求一樣,但是用在不同模塊的場(chǎng)景。

組件化開發(fā)之?dāng)?shù)據(jù)庫(kù)分離

比如,我現(xiàn)在開發(fā)的視頻模塊想要給別人用,由于緩存之類需要用到數(shù)據(jù)庫(kù),難道還要把這個(gè)lib還得依賴一個(gè)體積較大的第三方數(shù)據(jù)庫(kù)?但是使用系統(tǒng)原生sql數(shù)據(jù)庫(kù)又不太方便,怎么辦?暫時(shí)我也沒找到辦法……

4.8 組件化時(shí)資源名沖突

資源名沖突有哪些?

比如,color,shape,drawable,圖片資源,布局資源,或者anim資源等等,都有可能造成資源名稱沖突。這是為何了,有時(shí)候大家負(fù)責(zé)不同的模塊,如果不是按照統(tǒng)一規(guī)范命名,則會(huì)偶發(fā)出現(xiàn)該問題。

尤其是如果string, color,dimens這些資源分布在了代碼的各個(gè)角落,一個(gè)個(gè)去拆,非常繁瑣。其實(shí)大可不必這么做。因?yàn)閍ndroid在build時(shí),會(huì)進(jìn)行資源的merge和shrink。res/values下的各個(gè)文件(styles.xml需注意)最后都只會(huì)把用到的放到intermediate/res/merged/../valus.xml,無(wú)用的都會(huì)自動(dòng)刪除。并且最后我們可以使用lint來自動(dòng)刪除。所以這個(gè)地方不要耗費(fèi)太多的時(shí)間。

解決辦法

這個(gè)問題也不是新問題了,第三方SDK基本都會(huì)遇到,可以通過設(shè)置 resourcePrefix 來避免。設(shè)置了這個(gè)值后,你所有的資源名必須以指定的字符串做前綴,否則會(huì)報(bào)錯(cuò)。但是 resourcePrefix 這個(gè)值只能限定 xml 里面的資源,并不能限定圖片資源,所有圖片資源仍然需要你手動(dòng)去修改資源名。

個(gè)人建議

將color,shape等放到基礎(chǔ)庫(kù)組件中,因?yàn)樗械臉I(yè)務(wù)組件都會(huì)依賴基礎(chǔ)組件庫(kù)。在styles.xml需注意,寫屬性名字的時(shí)候,一定要加上前綴限定詞。假如說不加的話,有可能會(huì)在打包成aar后給其他模塊使用的時(shí)候,會(huì)出現(xiàn)屬性名名字重復(fù)的沖突,為什么呢?因?yàn)锽ezelImageView這個(gè)名字根本不會(huì)出現(xiàn)在intermediate/res/merged/../valus.xml里, 所以不要以為這是屬性的限定詞!

4.9 組件化開發(fā)遇到問題

如何做到各個(gè)組件化模塊能獲取到全局上下文

情景再現(xiàn)

比如,剛開始線上項(xiàng)目是在app主工程里創(chuàng)建的單利,那么在lib中或者后期劃分的組件化,是無(wú)法拿到主工程的application類中的上下文。這個(gè)時(shí)候可以

解決辦法

很容易,在lib里寫一個(gè)Utils工具類,然后在主工程application中初始化Utils.init(this),這樣就可以在lib和所有業(yè)務(wù)組件[已經(jīng)依賴公共基礎(chǔ)組件庫(kù)]中拿到全局上下文呢!

butterKnife使用問題

盡管網(wǎng)上有不少博客說可以解決butterKnife在不同組件之間的引用。但是我在實(shí)際開發(fā)時(shí),遇到組件模式和集成模式切換狀態(tài)時(shí),導(dǎo)致出現(xiàn)編譯錯(cuò)誤問題。要是那位在組件化中解決butterKnife引用問題,可以告訴我,非常感謝!

當(dāng)組件化是lib時(shí)

不能使用switch(R.id.xx),需要使用if..else來代替。

不要亂發(fā)bus消息

如果項(xiàng)目中大量的使用eventbus,那么會(huì)看到一個(gè)類中有大量的onEventMainThread()方法,寫起來很爽,閱讀起來很痛苦。

雖然說,前期使用EventBus或者RxBus發(fā)送消息來實(shí)現(xiàn)組件間通信十分方便和簡(jiǎn)單,但是隨著業(yè)務(wù)增大,和后期不斷更新,有的還經(jīng)過多個(gè)程序員前前后后修改,會(huì)使代碼閱讀量降低。項(xiàng)目中發(fā)送這個(gè)Event的地方非常多,接收這個(gè)Event的地方也很多。在后期想要改進(jìn)為組件化開發(fā),而進(jìn)行代碼拆分時(shí),都不敢輕舉妄動(dòng),生怕哪些事件沒有被接收。

頁(yè)面跳轉(zhuǎn)存在問題

如果一個(gè)頁(yè)面需要登陸狀態(tài)才可以查看,那么會(huì)寫if(isLogin()){//跳轉(zhuǎn)頁(yè)面}else{//跳轉(zhuǎn)到登錄頁(yè)面},每次操作都要寫這些個(gè)相同的邏輯。

原生startActivity跳轉(zhuǎn),無(wú)法監(jiān)聽到跳轉(zhuǎn)的狀態(tài),比如跳轉(zhuǎn)錯(cuò)誤,成功,異常等問題。

后時(shí)候,后臺(tái)會(huì)控制從點(diǎn)擊按鈕【不同場(chǎng)景下】跳轉(zhuǎn)到不同的頁(yè)面,假如后臺(tái)配置信息錯(cuò)誤,或者少了參數(shù),那么跳轉(zhuǎn)可能不成功或者導(dǎo)致崩潰,這個(gè)也沒有一個(gè)好的處理機(jī)制。

阿里推出的開源框架Arouter,便可以解決頁(yè)面跳轉(zhuǎn)問題,可以添加攔截,或者即使后臺(tái)配置參數(shù)錯(cuò)誤,當(dāng)監(jiān)聽到跳轉(zhuǎn)異常或者跳轉(zhuǎn)錯(cuò)誤時(shí)的狀態(tài),可以直接默認(rèn)跳轉(zhuǎn)到首頁(yè)。我在該開源案例就是這么做的!

關(guān)于跳轉(zhuǎn)參數(shù)問題

先來看一下這種代碼寫法,這種寫法本沒有問題,只是在多人開發(fā)時(shí),如果別人想要跳轉(zhuǎn)到你開發(fā)模塊的某個(gè)頁(yè)面,那么就容易傳錯(cuò)值。建議將key這個(gè)值,寫成靜態(tài)常量,放到一個(gè)專門的類中。方便自己,也方便他人。

//跳轉(zhuǎn)
intent.setClass(this,CommentActivity.class);
intent.putExtra("id",id);
intent.putExtra("allNum",allNum);
intent.putExtra("shortNum",shortNum);
intent.putExtra("longNum",longNum);
startActivity(intent);


//接收
Intent intent = getIntent();
int allNum = intent.getExtras().getInt("allNum");
int shortNum = intent.getExtras().getInt("shortNum");
int longNum = intent.getExtras().getInt("longNum");
int id = intent.getExtras().getInt("id");

5.組件間通信 5.1 選擇那個(gè)開源路由庫(kù)

比較有代表性的組件化開源框架有得到得到DDComponentForAndroid、阿里Arouter、聚美Router 等等。

得到DDComponentForAndroid:一套完整有效的android組件化方案,支持組件的組件完全隔離、多帶帶調(diào)試、集成調(diào)試、組件交互、UI跳轉(zhuǎn)、動(dòng)態(tài)加載卸載等功能。

阿里Arouter:對(duì)頁(yè)面、服務(wù)提供路由功能的中間件,簡(jiǎn)單且夠用好用,網(wǎng)上的使用介紹博客也很多,在該組件化案例中,我就是使用這個(gè)。

Router:一款單品、組件化、插件化全支持的路由框架

5.2 阿里Arouter基礎(chǔ)原理

這里只是說一下基礎(chǔ)的思路

在代碼里加入的@Route注解,會(huì)在編譯時(shí)期通過apt生成一些存儲(chǔ)path和activityClass映射關(guān)系的類文件,然后app進(jìn)程啟動(dòng)的時(shí)候會(huì)拿到這些類文件,把保存這些映射關(guān)系的數(shù)據(jù)讀到內(nèi)存里(保存在map里),然后在進(jìn)行路由跳轉(zhuǎn)的時(shí)候,通過build()方法傳入要到達(dá)頁(yè)面的路由地址。

添加@Route注解然后編譯一下,就可以生成這個(gè)類,然后看一下這個(gè)類。如下所示:

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$video implements IRouteGroup {
  @Override
  public void loadInto(Map atlas) {
    atlas.put("/video/VideoActivity", RouteMeta.build(RouteType.ACTIVITY, VideoActivity.class, "/video/videoactivity", "video", null, -1, -2147483648));
  }
}

ARouter會(huì)通過它自己存儲(chǔ)的路由表找到路由地址對(duì)應(yīng)的Activity.class(activity.class = map.get(path)),然后new Intent(),當(dāng)調(diào)用ARouter的withString()方法它的內(nèi)部會(huì)調(diào)用intent.putExtra(String name, String value),調(diào)用navigation()方法,它的內(nèi)部會(huì)調(diào)用startActivity(intent)進(jìn)行跳轉(zhuǎn),這樣便可以實(shí)現(xiàn)兩個(gè)相互沒有依賴的module順利的啟動(dòng)對(duì)方的Activity了。

看_ARouter類中的 _navigation方法代碼,在345行。

private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    final Context currentContext = null == context ? mContext : context;

    switch (postcard.getType()) {
        case ACTIVITY:
            // Build intent
            final Intent intent = new Intent(currentContext, postcard.getDestination());
            intent.putExtras(postcard.getExtras());

            // Set flags.
            int flags = postcard.getFlags();
            if (-1 != flags) {
                intent.setFlags(flags);
            } else if (!(currentContext instanceof Activity)) {    // Non activity, need less one flag.
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }

            // Set Actions
            String action = postcard.getAction();
            if (!TextUtils.isEmpty(action)) {
                intent.setAction(action);
            }

            // Navigation in main looper.
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    startActivity(requestCode, currentContext, intent, postcard, callback);
                }
            });

            break;
        case PROVIDER:
            //這里省略代碼
        case BOARDCAST:
        case CONTENT_PROVIDER:
        case FRAGMENT:
            //這里省略代碼
        case METHOD:
        case SERVICE:
        default:
            return null;
    }
    return null;
}

5.3 使用Arouter注意事項(xiàng)

使用阿里路由抽取工具類,方便后期維護(hù)!

首先看一下網(wǎng)絡(luò)上有一種寫法。

//首先通過注解添加下面代碼
@Route(path = "/test/TestActivity")
public class TestActivity extends BaseActivity {

}

//跳轉(zhuǎn)
ARouter.getInstance().inject("/test/TestActivity");

優(yōu)化后的寫法

下面這種做法,是方便后期維護(hù)。

//存放所有的路由路徑常量
public class ARouterConstant {
    //跳轉(zhuǎn)到視頻頁(yè)面
    public static final String ACTIVITY_VIDEO_VIDEO = "/video/VideoActivity";
    //省略部分diamagnetic
}

//存放所有的路由跳轉(zhuǎn),工具類
public class ARouterUtils {
    /**
     * 簡(jiǎn)單的跳轉(zhuǎn)頁(yè)面
     * @param string                string目標(biāo)界面對(duì)應(yīng)的路徑
     */
    public static void navigation(String string){
        if (string==null){
            return;
        }
        ARouter.getInstance().build(string).navigation();
    }
}

//調(diào)用
@Route(path = ARouterConstant.ACTIVITY_VIDEO_VIDEO)
public class VideoActivity extends BaseActivity {

}
ARouterUtils.navigation(ARouterConstant.ACTIVITY_VIDEO_VIDEO);

06.關(guān)于其他內(nèi)容介紹 6.1 關(guān)于博客匯總鏈接

1.技術(shù)博客匯總

2.開源項(xiàng)目匯總

3.生活博客匯總

4.喜馬拉雅音頻匯總

5.其他匯總

6.1 參考博客鏈接

Android徹底組件化方案實(shí)踐:https://www.jianshu.com/p/1b1...

教你打造一個(gè)Android組件化開發(fā)框架:https://blog.csdn.net/cdecde1...

Android組件化框架設(shè)計(jì)與實(shí)踐:https://www.jianshu.com/p/1c5...

知乎 Android 客戶端組件化實(shí)踐:https://www.jianshu.com/p/f1a...

聚美組件化實(shí)踐之路:https://juejin.im/post/5a4b44...

Android 組件化 —— 路由設(shè)計(jì)最佳實(shí)踐:https://www.jianshu.com/p/8a3...

6.2 關(guān)于我的博客

我的個(gè)人站點(diǎn):www.yczbj.org,www.ycbjie.cn

github:https://github.com/yangchong211

知乎:https://www.zhihu.com/people/...

簡(jiǎn)書:http://www.jianshu.com/u/b7b2...

csdn:http://my.csdn.net/m0_37700275

喜馬拉雅聽書:http://www.ximalaya.com/zhubo...

開源中國(guó):https://my.oschina.net/zbj161...

泡在網(wǎng)上的日子:http://www.jcodecraeer.com/me...

郵箱:[email protected]

阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV

segmentfault頭條:https://segmentfault.com/u/xi...

6.3 開源項(xiàng)目地址
組件化實(shí)踐項(xiàng)目開源地址:https://github.com/yangchong2...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73157.html

相關(guān)文章

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<