得到App Android元件化方案解析
為什麼要寫這篇文章
元件化已經深得客戶端開發大神們的認可,元件化確實進一步優化了客戶端團隊的開發效率,更適合大團隊分組推進,理解元件化的思想,深入研究元件化的實現方案是很必要的。
在接觸眾多元件化的方案後,自己也萌生了想搭建一套集眾家之所長的元件化方案。
而從【得到】元件化方案入手,主要是【得到】元件化開源的demo比較完整,日期也比較新。
這篇文章的主要目的是想研究一下【得到】元件化比其他元件化方案更優的點,就像得到官方文章中說的一樣:
在設計之初參考了目前已有的元件化和外掛化方案,站在巨人的肩膀上又加了一點自己的想法
綜合各家之所長,再加上自己的一點想法,整理出自己習慣的一套元件化方案
得到App元件化的大概介紹
開源的元件化方案並不少,放出來的元件化文章就更多了,但是像【得到】技術團隊這樣連同方案和解析文章全部一起打包,毫無保留的開源給開發者的確實是少數。
只是官方文章難免站在了主人的角度來詮釋框架的整體,對於一個剛剛接觸這套框架的學習者來說還是有點難懂,所以我這裡再次對這個方案的實現從一個學習者的角度梳理一下,對其他學習者應該也是有意義的。
【得到】元件化優缺點都比較明顯
- 優點:程式碼隔離方案很實用,並且邏輯簡單清晰好理解
- 缺點:暫時還沒有加入 Activity註解-apt自動生成-ui路由 ,釋出aar仍需改動isRunAlone的值。
【得到】元件化
從圖中我們可以看得出來
- componentservice基本就是base moudle
- 業務元件與主host之間靠gradle.properties確定依賴關係,但這裡只是依賴關係的標記而已,並沒有直接依賴上,也就是開發期元件之間是不能直接呼叫的,否則編譯會報錯,從根本上杜絕了直接呼叫的問題。
- 所有業務元件統一進入build-gradle處理,在編譯期自動新增依賴關係,也就是完成了【程式碼隔離】
【得到】元件化的程式碼隔離,主要靠兩個步驟
UI路由方案的解讀其實已經非常多,github上的明星專案就好幾個呢,所以我就不過多解讀了。
這裡我們著重瞭解一下【得到】元件化有關於程式碼隔離的實現方案。
本文很少貼大段的原始碼,主要是想用盡可能簡明的語音和清晰的圖來說明實現方法。所以最好是熟讀官網文章記錄下自己的問題之後,一邊對照我這篇解析文章,一邊對照著demo原始碼來理解才是最佳途徑。
主要靠兩個步驟
- build-gradle外掛
- componentservice服務介面
build-gradle的作用
從圖中可以看出包括主專案App在內的每一個moudle都通過在build.gradle檔案中
apply plugin: 'com.dd.comgradle'
引入自定義外掛build-gradle
而apply plugin 實際上是呼叫的gradle的project中的方法
void apply(Map<String, ?> options)
如果你疑問為什麼呼叫方法沒有括號,沒有引數,請先了解一下Gradle DSl的基礎知識和語法特性。深入理解Android之Gradle
每個moudle都呼叫了apply方法後,實際上都執行到了ComBuild.groovy中的
void apply(Project project) {}
至此就算明白了 每個moudle在編譯期都會執行到ComBuild.groovy的apply方法中。
apply方法中做了這些事請:
- 呼叫getTaskInfo()方法,讀取當前Task資訊,標記是否是組裝(assemble)Task,是否是除錯(DEBUG)模式
- 如果是Assemble(組裝類)的Task,呼叫fetchMainmodulename()方法,確定當前編譯的moudle,從rootProject中讀取mainmodulename的初始值
從gradle.properties中取出isRunAlone的值,根據isRunAlone執行不同的操作
- 如isRunAlone=true,則代表當前的moudle就是主專案,引入plugin: ‘com.android.application’外掛
如果當前moudle是否是主moudle,且當前task是否為Assemble(組裝類)Task,則根據gradle.properties中的debugComponent和compileComponent分情況自動新增依賴檔案,元件釋出的aar就是這裡使用的。
根據每個moudle的build.gradle檔案中isRegisterCompoAuto欄位判斷是否自動註冊moudle所提供的服務,這裡涉及到何時呼叫每個moudle中的IApplicationLike,什麼方式呼叫?
呼叫的方式有兩種:
A. 編譯期位元組碼插入的方式,就是掃描所有的ApplicationLike類後,自動寫入呼叫程式碼,具體檢視ComCodeTransform.groovy中的程式碼邏輯
B. 反射呼叫,類似下面的程式碼
Router.registerComponent("com.mrzhang.reader.applike.ReaderAppLike")
- 如isRunAlone=false,則代表當前的moudle是library,引入plugin: ‘com.android.library’外掛,然後看是否是assembleRelease Task:
如果是assembleRelease,則釋出元件aar
如果不是assembleRelease Task,則把當前moudle作為library隨同主專案一起編譯而已
- 如isRunAlone=true,則代表當前的moudle就是主專案,引入plugin: ‘com.android.application’外掛
componentservice服務介面的實現
相比build-gradle外掛,componentservice服務介面的實現就非常簡單了,官網部落格中說是面向介面程式設計還是非常恰當的,如下圖所示:
- 從圖中可以看出,componentservice中的Router存放的所有元件所提供的服務,而Router本身只是一個map而已,原理非常簡單
- 元件B的開發人員再編寫好元件B之後,需要提供介面文件
- 元件A的開發人員想用元件B的服務時,就需要根據介面文件有關於元件B的服務描述中,找到元件B所提供的服務的名字,然後呼叫
router.getService(服務全路徑名);
即可獲取響應的服務 - 而面向介面程式設計其實說的是每個應用到服務的地方都是針對父類程式設計,以達到介面和實現的分離目的
- 由於在開發期是面對存放在componentservice目錄下的服務父類程式設計,而在提供服務的元件中的IApplicationLike中註冊了元件中關於服務的具體實現。
上面的文字描述可能還是不太明白,所以還是用圖表示好了
總結
這是我對於【得到】元件化方案中的程式碼隔離部分的一個梳理,可能有些地方描述不清楚或者有理解錯誤,歡迎大家指正。
如有問題可以一起討論,個人qq 278960878.
希望對大家有幫助!