從工程領域來看,模塊化、組件化、插件化三種技術都是指將複雜代碼進行拆分,達到解偶分層、便於管理的目的。廣泛意義上,將代碼按照業務模塊劃分就是模塊化,若是再進一步從模塊化代碼中抽出通用於全部App的組件,做爲一個獨立的module或者maven依賴(好比一些比較有名的第三方SDK),這個組件生成的過程就叫組件化。插件化則是指將App按必定規則拆分紅幾個若干個APK,除了主APK,其餘APK都可以經過網絡下發而後經過主APK加載。經過加載、修改、卸載非主APK,必定程度上給予了APP熱修復的功能。然而隨着Android 9.0上私有API的限制,插件化受到了極大的限制,主流方案慢慢向穩定、務實的的組件化方案演進。java
比較傳統的一些架構是利用MVC、MVP、MVVM對項目進行分包,然而隨着項目代碼量愈來愈多,修改的時候會牽一髮而動全身,並且不利於並行開發和迴歸測試。經過將一些通用的代碼進行拆分,而後使用maven依賴進來,能夠減小本地的代碼量並解偶了一部分維護工做。隨着業務的日漸複雜,aar級別的組件化還不夠,每次修改部分業務都會須要對其餘相關業務進行進行迴歸測試,一些當前版本不會用到的代碼仍然會打包進APk,這樣不利於後續的維護。因此業界相繼提出了「組件化架構」的思想,也就是在整個Project層面對代碼按照模業務塊、功能模塊、基礎模塊進行劃分,而後在具體的Module中用MVC、MVP、MVVM等思想構建具體的代碼。以下圖所示:android
其中基礎組件層主要包括:網絡庫、日誌庫、路由庫等。但在實際的開發過程當中,每每須要對這些基礎庫封裝一層,好比對網絡庫用觀察者模式封裝一層來實現UI移步加載、路由須要自定義攔截器等,這一層就是咱們的service層,也就是功能組件層。Service層的主要目的是向外提供服務,而業務組件則是具體的業務邏輯。具體的分包示意圖以下:bash
爲了不循環依賴和業務邏輯之間的交叉,同一層的組件是不能直接相互引用的。 這是由於: 1)除了主Module,其餘任何一個業務組件均可能是處於加載或者不加載的狀態,好比有2個模塊A和B,若是相互依賴,且假設沒有加載B,而線上模塊A使用了B中功能,那麼A可能會crash; 2)任何一個業務組件都是獨立的,也就是說多是由不一樣的部門並行開發的,不該該互相依賴。 鑑於這兩個規則,同一層之間是不能直接通訊(若是隻考慮第1點,不考慮獨立依賴,可使用反射,可是不夠美觀且會影響性能)。 這個通訊包括兩方面: 1)界面之間的相互跳轉; 2)服務之間及業務之間的相互調用。同時組件如何註冊、加載、卸載,這些都是組件化架構須要解決的。網絡
結合上述的理論基礎,在實踐過程當中須要解決的技術難點主要有:模塊間的通訊、路由表的自動維護、組件的生命週期管理、主包管理及進程間通訊等。架構
說到通訊,咱們能想到的方案有兩種,路由和事件總線。路由能夠解決界面的跳轉和一些dialog、toast的顯隱,可是不能解決服務之間的相互調用和回調。業界提出了相似於Android中四大組件之一ServiceManager的處理方法--「接口下沉」,也就是在基礎組件層新建一個ServiceManager,並提供通用服務接口IService,在須要暴露服務的地方實現該接口並手動/自動註冊到ServiceManager中,這樣任何須要該服務的地方均可以經過:ServiceManager.get(Classclz)靜態工廠方法取得,相似於ServiceManager中的addService(String name, IBinder service)和IBinder getService(String name)方法,固然ServiceManager類徹底能夠由註解來生成。第二種方案就是使用事件總線,好比EventBus或者RxBus,由於事件總線自己是經過觀察者模式實現的同時能夠支持跳轉,因此也能夠用來替代路由+「接口下沉」的方案。app
在實踐過程當中,現有的方案都須要維護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而不須要損耗性能。這種方式配合後臺下發的方式,就能保住靈活性。框架
大體步驟以下:異步
1.新建buildSrc工程,或者獨立工程;maven
2.接入Transform依賴:implementation 'com.android.tools.build:gradle:3.2.1',值得注意的是這個包裏面包含了ASM庫;ide
3.新建一個class實現Transform類,將掃描範圍設置爲:TransformManager.SCOPEFULLPROJECT,並在tranform(TransformInvocation transformInvocation)中遍歷目錄輸入和jar輸入,並使用ClassVisitor操做字節碼;
4.最後在自定義Plugin中註冊這個自定義Transform。
具體的操做細節能夠參考官方文檔。
一個進程對應着一個虛擬機,虛擬機須要管理APk的生命週期。同理,若是把APP看做一個虛擬機,把各個業務組件當作是小型的APP,那麼APP是須要妥善管理各個業務組件的生命週期的。也就是說咱們須要同步資源初始化、使用、銷燬的時機。能夠模擬虛擬機的工做流程:加載-驗證-準備-解析-初始化-使用-卸載,同時使用ApplicationDelegate代理,hook住Application的各個生命週期,這樣就能夠實現組件的同步加載,須要主動銷燬時,則能夠將module手動卸載。
隨着拆分出來的Module和aar愈來愈多,每次都須要重複配置依賴和項目基本參數。因爲每層(主Module層、業務組件層、功能組件層、基礎組件層)之間的依賴都大同小異的,所以抽出一層gradle_component,專門用來配置通用gradle,這樣就能夠統一依賴,好比:
而後把這些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
}
複製代碼
進程是最小的資源分配管理單位,當業務組件多大必定的程度時,會須要考慮使用多進程通訊。若是隻是簡單的跳轉不涉及到數據的獲取,那麼路由組件是能夠勝任的,由於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而互不干擾,並且其可擴展性也比較強,對業務不斷擴大的項目是一個不錯的選擇。
整理的一些相關架構及資料(關注主頁)
喜歡文章的點個贊鼓勵一下叭~