Android組件化架構實踐

引言

從工程領域來看,模塊化、組件化、插件化三種技術都是指將複雜代碼進行拆分,達到解偶分層、便於管理的目的。普遍意義上,將代碼按照業務模塊劃分就是模塊化,如果再進一步從模塊化代碼中抽出通用於所有App的組件,作為一個獨立的module或者maven依賴(比如一些比較有名的第三方SDK),這個組件生成的過程就叫組件化。插件化則是指將App按一定規則拆分成幾個若干個APK,除了主APK,其他APK均可以通過網絡下發然後通過主APK加載。通過加載、修改、卸載非主APK,一定程度上給予了APP熱修復的功能。然而隨著Android 9.0上私有API的限制,插件化受到了極大的限制,主流方案慢慢向穩定、務實的的組件化方案演進。

Android組件化架構實踐

組件化架構

比較傳統的一些架構是利用MVC、MVP、MVVM對項目進行分包,然而隨著項目代碼量越來越多,修改的時候會牽一髮而動全身,而且不利於並行開發和迴歸測試。通過將一些通用的代碼進行拆分,然後使用maven依賴進來,可以減少本地的代碼量並解偶了一部分維護工作。隨著業務的日漸複雜,aar級別的組件化還不夠,每次修改部分業務都會需要對其他相關業務進行進行迴歸測試,一些當前版本不會用到的代碼仍然會打包進APk,這樣不利於後續的維護。所以業界相繼提出了“組件化架構”的思想,也就是在整個Project層面對代碼按照模業務塊、功能模塊、基礎模塊進行劃分,然後在具體的Module中用MVC、MVP、MVVM等思想構建具體的代碼。如下圖所示:

Android組件化架構實踐

其中基礎組件層主要包括:網絡庫、日誌庫、路由庫等。但在實際的開發過程中,往往需要對這些基礎庫封裝一層,比如對網絡庫用觀察者模式封裝一層來實現UI移步加載、路由需要自定義攔截器等,這一層就是我們的service層,也就是功能組件層。Service層的主要目的是向外提供服務,而業務組件則是具體的業務邏輯。具體的分包示意圖如下:

Android組件化架構實踐

為了避免循環依賴和業務邏輯之間的交叉,同一層的組件是不能直接相互引用的。

這是因為:

1)除了主Module,其他任何一個業務組件都可能是處於加載或者不加載的狀態,比如有2個模塊A和B,如果相互依賴,且假設沒有加載B,而線上模塊A使用了B中功能,那麼A可能會crash;

2)任何一個業務組件都是獨立的,也就是說可能是由不同的部門並行開發的,不應該互相依賴。

鑑於這兩個規則,同一層之間是不能直接通信(如果只考慮第1點,不考慮獨立依賴,可以使用反射,但是不夠美觀且會影響性能)。

這個通信包括兩方面:

1)界面之間的相互跳轉;

2)服務之間及業務之間的相互調用。同時組件如何註冊、加載、卸載,這些都是組件化架構需要解決的。

實踐方案

結合上述的理論基礎,在實踐過程中需要解決的技術難點主要有:模塊間的通信、路由表的自動維護、組件的生命週期管理、主包管理及進程間通信等。

1. 通信

說到通信,我們能想到的方案有兩種,路由和事件總線。路由可以解決界面的跳轉和一些dialog、toast的顯隱,但是不能解決服務之間的相互調用和回調。業界提出了類似於Android中四大組件之一ServiceManager的處理方法--“接口下沉”,也就是在基礎組件層新建一個ServiceManager,並提供通用服務接口IService,在需要暴露服務的地方實現該接口並手動/自動註冊到ServiceManager中,這樣任何需要該服務的地方都可以通過:ServiceManager.get(Classclz)靜態工廠方法取得,類似於ServiceManager中的addService(String name, IBinder service)和IBinder getService(String name)方法,當然ServiceManager類完全可以由註解來生成。第二種方案就是使用事件總線,比如EventBus或者RxBus,因為事件總線本身是通過觀察者模式實現的同時可以支持跳轉,所以也可以用來替代路由+“接口下沉”的方案。

Android組件化架構實踐

2. 自動註冊

在實踐過程中,現有的方案都需要維護3個HashMap,分別是路由表、服務表以及組件表,當服務多到一定的程度,手動維護3個哈希表是一場災難。以小贏理財現有的Scheme路由庫為例,初代版本中是在內存中維護一個靜態的HashMap路由表,key表示路徑,value代表calss。雖然這樣方便省事但是可維護性較差,一旦跳轉規則變化或者忘記及時更新則會失效。秉承著“能讓機器完成絕不自己動手”的原則,在迭代中改成了通過反射去掃描AndroidManifest,自動生成維護HashMap,這樣可以做到map的自動註冊。但是由於掃描是在Application的onCreare()方法中完成,用AOP測試發現掃描過程比較耗時,這種自動化的方式是以犧牲啟動時間為代價的。聯想到註解,可以通過編譯時註解插入代碼動態生成HashMap,但嘗試過後發現部分場景下行不通,因為編譯時註解的特性只在源碼編譯時生效,無法掃描到aar包裡的註解,這種情況不適合遠程預埋aar,動態下發的場景。運行時註解也會不可避免的會造成性能的缺失。幸運的是,Android官方提供了Transform API,可以用來在.class轉換為.dex前操作class文件。這樣配合ASM(如果覺得javap命令生成字節碼太麻煩,可以使用IntelliJ IDEA插件‘Bytecode outline’,可方便快捷根據java類生成字節碼)或javassist就可以動態的修改字節碼,從而動態生成HashMap而不需要損耗性能。這種方式配合後臺下發的方式,就能保住靈活性。

Android組件化架構實踐

大致步驟如下:

  1. 新建buildSrc工程,或者獨立工程;
  2. 接入Transform依賴:implementation 'com.android.tools.build:gradle:3.2.1',值得注意的是這個包裡面包含了ASM庫;
  3. 新建一個class實現Transform類,將掃描範圍設置為:TransformManager.SCOPEFULLPROJECT,並在tranform(TransformInvocation transformInvocation)中遍歷目錄輸入和jar輸入,並使用ClassVisitor操作字節碼;
  4. 最後在自定義Plugin中註冊這個自定義Transform。

具體的操作細節可以參考官方文檔。

3. 組件生命週期管理

一個進程對應著一個虛擬機,虛擬機需要管理APk的生命週期。同理,如果把APP看作一個虛擬機,把各個業務組件看成是小型的APP,那麼APP是需要妥善管理各個業務組件的生命週期的。也就是說我們需要同步資源初始化、使用、銷燬的時機。可以模擬虛擬機的工作流程:加載-驗證-準備-解析-初始化-使用-卸載,同時使用ApplicationDelegate代理,hook住Application的各個生命週期,這樣就可以實現組件的同步加載,需要主動銷燬時,則可以將module手動卸載。

Android組件化架構實踐

4. 依賴和主包管理

隨著拆分出來的Module和aar越來越多,每次都需要重複配置依賴和項目基本參數。由於每層(主Module層、業務組件層、功能組件層、基礎組件層)之間的依賴都大同小異的,因此抽出一層gradle_component,專門用來配置通用gradle,這樣就可以統一依賴,比如:

Android組件化架構實踐

然後把這些gradle分別apply到對應的層級,當然為了方便管理減少.gradle文件,可以將具體的依賴通過自定Plugin的形式注入。由於module和aar比較多,當真正進行build的時候,需要檢查settings.gradle並對每個Module進行初始化配置,再運行具體的task進行打包。這個操作隨著Module個數的增多,執行的耗時會直線上升。實際情況是,假設只修改了某個module,並只想運行這一部分增量代碼,這個時候可以通過切換主APP完成。由於Android項目是通過plugin來識別的:

apply plugin:'com.android.application' // 主module
apply plugin: 'com.android.library' // module

這樣我們可以在本地自定義一個'isMainAPP'參數來控制主Module和Module的切換。

if(isMainAPP) { // 切換
apply plugin:'com.android.application' // 主module
} else {
apply plugin: 'com.android.library' // module
}

5. 多進程通信

進程是最小的資源分配管理單位,當業務組件多大一定的程度時,會需要考慮使用多進程通信。如果只是簡單的跳轉不涉及到數據的獲取,那麼路由組件是可以勝任的,因為Android內置的Intent機制本來就是跨進程的。如果需要同步數據,則需要考慮進程通信中出現的髒數據,比如同時操作sharepreferences是比較棘手的,因為sharepreferences在文件和內存中各有一份數據,且有時候不相同。Android系統提供了基於mmap的Binder通信機制,落實到工程代碼就是實現AIDL,生成遠程Binder類和當前進程的Proxy類,並定義相關的Service和BinderPool。但是這樣稍微有點重,可以考慮使用現有的ContentProvider + SQLite(ContentProvider本身也是AIDL通信機制,只是系統對其進行了一層封裝),在路由庫中增加對多進程通信攔截鏈的支持。

已有的解決方案

當前的一些大公司都先後開源了自己的組件化架構框架,比較知名的有美團的modular-event,阿里的ARouter以及得到(邏輯思維主打APP)的DDComponentForAndroid。其設計思想大同小異,基本也是機遇以上的設計要點,輔助一些同步和異步功能。美團捨棄了之前開發的“WMRouter”路由,轉而使用了modular-event,阿里和得到則是使用路由+接口下沉的方式去構建整個架構。“組件化架構”能夠清晰的劃分項目結構,嚴格的將代碼根據“業務組件”、“模塊組件”、“基礎組件”進行劃分,各個項目組成員可以並行開發module而互不干擾,而且其可擴展性也比較強,對業務不斷擴大的項目是一個不錯的選擇。

最後相關架構及資料

Android組件化架構實踐

組件化框架設計.png


Android組件化架構實踐

Android高級技術大綱

領取方式:

關注+私信回覆“安卓資料”免費獲取!

領取獲取往期Android高級架構資料、源碼、筆記、視頻。高級UI、性能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)微信小程序、Flutter全方面的Android進階實踐技術。


分享到:


相關文章: