引言
先簡單介紹一下Android插件化。很早之前已經有公司在研究這項技術,淘寶做得比較早,但淘寶的這項技術一直是保密的。直到2015年才陸續出現很多框架,Android插件化分成很多技術流派,實現的方式都不太一樣。
發展歷史
首先,要記住2012年這個時間點。2012年的時候,就有人做插件化技術,是大眾點評的屠毅敏,他推出了AndroidDynamicLoader框架,用Fragment來實現。大眾點評是國內做App比較早的公司,他們積累了很多的經驗,尤其是插件化技術 。通過動態加載不同的Fragement,把想換的頁面都換掉。我們也是在這個項目中第一次看到了如何通過addAssetPath來讀取插件中的資源。
2013年,出現了23Code。23Code提供了一個殼,在這個殼里可以動態下載插件,然后動態運行。可以在殼外編寫各種各樣的控件,放在這個框架下去運行。這就是Android插件化技術。這個項目的作者和開源地址,目前不是很清楚。
2014年初,大家也許看過一個視頻,阿里一位員工做了一次技術分享,專門講淘寶的Altas技術,以及這項技術的大方向。但是很多技術細節沒有分享。
然后是任玉剛的里程碑式的項目。2014年底,玉剛發布了一個Android插件化項目,起名為dynamic-load-apk,這跟后續介紹的很多插件化項目都不太一樣。它沒有Hook太多的系統底層方法,而是從上層,即App應用層解決問題,創建一個繼承自Activity的ProxyActivity類,然后讓插件中的所有Activity都繼承自ProxyActivity,并重寫Activity所有的方法。之所以說這個項目是里程碑式的,是因為在2015年之前業界沒有太多資料可以參考。
2015年4月,一個新框架推出來,叫OpenAltas,后來改名為ACDD。這個框架參考了淘寶App的很多經驗,主要就是Hook的思想,同時,還首次提出來通過擴展AAPT來解決插件與宿主的資源id沖突的問題。
2015年8月,張勇發布DroidPlugin。這是Android插件化中第二個里程碑式的項目,這個項目太牛了,能把任意的App都加載到宿主里??梢曰谶@個框架寫一個宿主App,然后就可以把別人寫的App都當作插件來加載。這個框架的功能的確很強大,但強大的代價就是要改寫很多Android系統的底層代碼,更別提這哥們還比較懶,沒有制訂任何說明文檔,導致技術人員掌握這個框架不太容易。
再之后就是百花齊放的時代了,GitHub上有很多插件化框架,但這些框架影響都不大,我們這里就略過了。
接下來登場的是熱修復技術。2015年5月,iOS推出了JSPatch,JSPatch通過Runtime的機制,能迅速修復線上App任何一個類的任何一個方法。而當時的Android系統沒有能迅速替換的方式。于是,在2015年9月,有人找到了實現迅速替換的途徑,就是Andfix,后面會講它的原理。
2015年10月,大眾點評的賈吉鑫做了一個項目,起名為Nuwa(女媧),主要思路跟Andfix差不多,都是解決Android的修復問題,能修復線上的任何一個方法??上Ш髞頉]有繼續維護。
2015年底,仍然是Android插件化框架,福建的林廣亮提出了一個新機制——Small框架,這個機制不太一樣的地方就是,通過腳本的方式來解決資源沖突的問題。
2015 年 8 月,DroidPlugin 是 360 手機助手實現的一種插件化框架,它可以直接運行第三方的獨立 APK 文件,完全不需要對 APK 進行修改或安裝。一種新的插件機制,一種免安裝的運行機制,是一個沙箱(但是不完全的沙箱。就是對于使用者來說,并不知道他會把 apk 怎么樣), 是模塊化的基礎。
2017年 6 月 ,VirtualAPK 是滴滴開源的一套插件化框架,支持幾乎所有的 Android 特性,四大組件方面。
2017 年 7 月,RePlugin是一套完整的、穩定的、適合全面使用的,占坑類插件化方案,由360手機衛士的RePlugin Team研發,也是業內首個提出”全面插件化“(全面特性、全面兼容、全面使用)的方案。
基礎
介紹完Android插件化的歷史,接下來講一講Android插件化需要的Android系統底層知識。在座的基本都是做Android開發出身,或許有一半到三分之一是資深的,還有的只做了一兩年,希望對插件化有更深的認識。要想完全明白插件化技術,首先需要了解Android系統的底層實現。
首先,做Android系統原代碼的人應該非常熟悉Binder,如果沒有它真的寸步難行。Binder涉及兩層技術。你可以認為它是一個中介者模式,在客戶端和服務器端之間,Binder就起到中介的作用,這是我這段時間對Binder的思考。要實現四大組件的插件化,就需要在Binder上做修改。Binder服務端的內容沒辦法修改,只能改客戶端的代碼。四大組件每個組件的客戶端都不太一樣,這個需要大家自己去發現,時間關系,這里就不多說了。
學習Binder的最好方式就是AIDL。你可以讀到很多關于AIDL的資料,通過制訂一個aidl文件自動生成一個Java類,研究一下這個Java類的每個方法和變量,然后再反過來看四大組件,其實都是跟AIDL差不多的方式。
其次,是App打包的流程。代碼寫完了,執行一次打包操作,中途經歷了資源打包、dex生成、簽名等過程。其中最重要的就是資源的打包,即AAPT這一步,如果宿主和插件的資源id沖突,一種解決辦法就是在這里做修改。
第三,App在手機上的安裝流程也很重要。熟悉安裝流程不僅對插件化有幫助,在遇到安裝bug的時候也非常重要。手機安裝App的時候,經常會有下載異常,提示資源包不能解析,這時需要知道安裝App的這段代碼在什么地方,這只是第一步。第二步需要知道,App下載到本地后,具體要做哪些事情。手機有些目錄不能訪問,App下載到本地之后,放到哪個目錄下,然后會生成哪些文件。插件化有個增量更新的概念,如何下載一個增量包,從本地具體哪個位置取出一個包,這個包的具體命名規則是什么,等等。這些細節都必須要清楚明白。
第四,是App的啟動流程。Activity啟動有幾種方式?一種是寫一個startActivity,第二種是點擊手機App,通過手機系統里的Launcher機制,啟動App里默認的Activity。通常,App開發人員喜聞樂見的方式是第二種。那么第一種方式的啟動原理是什么呢?另外,啟動的時候,main函數在哪里?這個main函數的位置很重要,我們可以對它所在的類做修改,從而實現插件化。
第五點更重要,做Android插件化需要控制兩個地方。首先是插件Dex的加載,如何把插件Dex中的類加載到內存?另外是資源加載的問題。插件可能是apk也可能是so格式,不管哪一種,都不會生成R.id,從而沒辦法使用。這個問題有好幾種解決方案。一種是是重寫Context的getAsset、getResource之類的方法,偷換概念,讓插件讀取插件里的資源,但缺點就是宿主和插件的資源id會沖突,需要重寫AAPT。另一種是重寫AMS中保存的插件列表,從而讓宿主和插件分別去加載各自的資源而不會沖突。第三種方法,就是打包后,執行一個腳本,修改生成包中資源id。
第六點,在實施插件化后,如何解決不同插件的開發人員的工作區問題。比如,插件1和插件2,需要分別下載哪些代碼,如何獨立運行?就像機票和火車票,如何只運行自己的插件,而不運行別人的插件?這是協同工作的問題。火車票和機票,這兩個Android團隊的各自工作區是不一樣的,這時候就要用到Gradle腳本了,每個項目分別有各自的倉庫,有各自不同的打包腳本,只需要把自己的插件跟宿主項目一起打包運行起來,而不用引入其他插件,還有更厲害的是,也可以把自己的插件當作一個App來打包并運行。
上面介紹了插件化的入門知識,一共六點,每一點都需要花大量時間去理解。否則,在面對插件化項目的時候,很多地方你會一頭霧水。而只要理解了這六點核心,一切可迎刃而解。
類庫
1.DroidPlugin
是360手機助手在Android系統上實現了一種新的插件機制
2.Android-Plugin-Framework
此項目是Android插件開發框架完整源碼及示例。用來通過動態加載的方式在宿主程序中運行插件APK。
3.Small
世界那么大,組件那么小。Small,做最輕巧的跨平臺插件化框架。里面有很詳細的文檔
4.dynamic-load-apk
Android 使用動態加載框架DL進行插件化開發
5.AndroidDynamicLoader
Android 動態加載框架,他不是用代理 Activity 的方式實現而是用 Fragment 以及 Schema 的方式實現
6.DynamicAPK
實現Android App多apk插件化和動態加載,支持資源分包和熱修復.攜程App的插件化和動態加載框架.
7.ACDD
非代理Android動態部署框架
8.android-pluginmgr
不需要插件規范的apk動態加載框架。
9. VirtualAPK
VirtualAPK是滴滴出行自研的一款優秀的插件化框架。
10.android-pluginmgr
不需要插件規范的apk動態加載框架。
11.RePlugin
RePlugin是一套完整的、穩定的、適合全面使用的,占坑類插件化方案,由360手機衛士的RePlugin Team研發,也是業內首個提出”全面插件化“(全面特性、全面兼容、全面使用)的方案。
主流框架
在 Android 中實現插件化框架,需要解決的問題主要如下:
資源和代碼的加載
Android 生命周期的管理和組件的注冊
宿主 APK 和插件 APK 資源引用的沖突解決
下面分析幾個目前主流的開源框架,看看每個框架具體實現思路和優缺點。
DL 動態加載框架 ( 2014 年底)
GItHub:https://github.com/singwhatiwanna/dynamic-load-apk
DL支持的功能
1、plugin無需安裝即可由宿主調起。
2、支持用R訪問plugin資源
3、plugin支持Activity和FragmentActivity(未來還將支持其他組件)
4、基本無反射調用
5、插件安裝后仍可獨立運行從而便于調試
6、支持3種plugin對host的調用模式:
(1)無調用(但仍然可以用反射調用)。
(2)部分調用,host可公開部分接口供plugin調用。 這前兩種模式適用于plugin開發者無法獲得host代碼的情況。
(3)完全調用,plugin可以完全調用host內容。這種模式適用于plugin開發者能獲得host代碼的情況。
7、只需引入DL的一個jar包即可高效開發插件,DL的工作過程對開發者完全透明
8、支持android2.x版本
DL框架原理
動態加載主要有兩個需要解決的復雜問題:資源的訪問和activity生命周期的管理,除此之外,還有很多坑爹的小問題,而DL框架很好地解決了這些問題。需要說明的一點是,我們不可能調起任何一個未安裝的apk,這在技術上是很難實現的,我們調起的apk必須受某種規范的約束,只有在這種約束下開發的apk,我們才能將其調起。
DroidPlugin ( 2015 年 8 月)
Github:https://github.com/DroidPluginTeam/DroidPlugin
DroidPlugin 是 360 手機助手實現的一種插件化框架,它可以直接運行第三方的獨立 APK 文件,完全不需要對 APK 進行修改或安裝。一種新的插件機制,一種免安裝的運行機制,是一個沙箱(但是不完全的沙箱。就是對于使用者來說,并不知道他會把 apk 怎么樣), 是模塊化的基礎。
DroidPlugin 插件機制 :它可以在無需安裝、修改的情況下運行APK文件,此機制對改進大型APP的架構,實現多團隊協作開發具有一定的好處。
項目新地址:DroidPlugin
定義:
HOST程序:插件的宿主。
插件:免安裝運行的APK
限制和缺陷:
- 無法在插件中發送具有自定義資源的
Notification
,例如:
a. 帶自定義RemoteLayout的Notification
b. 圖標通過R.drawable.XXX指定的通知(插件系統會自動將其轉化為Bitmap) - 無法在插件中注冊一些具有特殊Intent Filter的
Service
、Activity
、BroadcastReceiver
、ContentProvider
等組件以供Android系統、已經安裝的其他APP調用。 - 缺乏對Native層的Hook,對某些帶native代碼的apk支持不好,可能無法運行。比如一部分游戲無法當作插件運行。
特點:
- 支持Androd 2.3以上系統
- 插件APK完全不需做任何修改,可以獨立安裝運行、也可以做插件運行。要以插件模式運行某個APK,你無需重新編譯、無需知道其源碼。
- 插件的四大組件完全不需要在Host程序中注冊,支持Service、Activity、BroadcastReceiver、ContentProvider四大組件
- 插件之間、Host程序與插件之間會互相認為對方已經"安裝"在系統上了。
- API低侵入性:極少的API。HOST程序只是需要一行代碼即可集成Droid Plugin
- 超強隔離:插件之間、插件與Host之間完全的代碼級別的隔離:不能互相調用對方的代碼。通訊只能使用Android系統級別的通訊方法。
- 支持所有系統API
- 資源完全隔離:插件之間、與Host之間實現了資源完全隔離,不會出現資源竄用的情況。
- 實現了進程管理,插件的空進程會被及時回收,占用內存低。
- 插件的靜態廣播會被當作動態處理,如果插件沒有運行(即沒有插件進程運行),其靜態廣播也永遠不會被觸發。
Small ( 2015 年底)
Github:https://github.com/wequick/Small
官網:http://code.wequick.net/Small
Small 是一種實現輕巧的跨平臺插件化框架,基于“輕量、透明、極小化、跨平臺”的理念
優點如下:
1.所有插件支持內置宿主包中。
2.插件的編碼和資源文件的使用與普通開發應用沒有差別。
3.通過設定 URI ,宿主以及 Native 應用插件,Web 插件,在線網頁等能夠方便進行通信。
4.支持 Android 、 iOS 、和 Html5 ,三者可以通過同一套 Javascript 接口實現通信。
缺點如下:
暫不支持 Service 的動態注冊,不過這個可以通過將 Service 預先注冊在宿主的 AndroidManifest.xml 文件中進行規避,因為 Service 的更新頻率通常非常低。
VirtualAPK (2017年 6 月 )
VirtualAPK 是滴滴開源的一套插件化框架,支持幾乎所有的 Android 特性,四大組件方面。
原理:
VirtualAPK 對插件沒有額外的約束,原生的 apk 即可作為插件。插件工程編譯生成 apk后,即可通過宿主 App 加載,每個插件 apk 被加載后,都會在宿主中創建一個單獨的 LoadedPlugin 對象。如下圖所示,通過這些 LoadedPlugin 對象,VirtualAPK 就可以管理插件并賦予插件新的意義,使其可以像手機中安裝過的 App 一樣運行。
- 合并宿主和插件的ClassLoader 需要注意的是,插件中的類不可以和宿主重復
- 合并插件和宿主的資源 重設插件資源的 packageId,將插件資源和宿主資源合并
- 去除插件包對宿主的引用 構建時通過 Gradle 插件去除插件對宿主的代碼以及資源的引用
特性如下:
四大組件均不需要在宿主manifest中預注冊,每個組件都有完整的生命周期。
1.Activity:支持顯示和隱式調用,支持Activity的theme和LaunchMode,支持透明主題;
2.Service:支持顯示和隱式調用,支持Service的start、stop、bind和unbind,并支持跨進程bind插件中的Service;
3.Receiver:支持靜態注冊和動態注冊的Receiver;
4.ContentProvider:支持provider的所有操作,包括CRUD和call方法等,支持跨進程訪問插件中的Provider。
5.自定義View:支持自定義 View,支持自定義屬性和style,支持動畫;
6.PendingIntent:支持PendingIntent以及和其相關的Alarm、Notification和AppWidget;
7.支持插件Application以及插件manifest中的meta-data;
8.支持插件中的so。
優秀的兼容性
- 兼容市面上幾乎所有的Android手機,這一點已經在滴滴出行客戶端中得到驗證。
- 資源方面適配小米、Vivo、Nubia 等,對未知機型采用自適應適配方案。
- 極少的 Binder Hook,目前僅僅 hook了兩個Binder:AMS和IContentProvider,hook 過程做了充分的兼容性適配。
- 插件運行邏輯和宿主隔離,確保框架的任何問題都不會影響宿主的正常運行。
RePlugin (2017 年 7 月)
GitHub:https://github.com/Qihoo360/RePlugin
RePlugin是一套完整的、穩定的、適合全面使用的,占坑類插件化方案,由360手機衛士的RePlugin Team研發,也是業內首個提出”全面插件化“(全面特性、全面兼容、全面使用)的方案。
其主要優勢有:
- 極其靈活:主程序無需升級(無需在Manifest中預埋組件),即可支持新增的四大組件,甚至全新的插件
- 非常穩定:Hook點僅有一處(ClassLoader),無任何Binder Hook!如此可做到其崩潰率僅為“萬分之一”,并完美兼容市面上近乎所有的Android ROM
- 特性豐富:支持近乎所有在“單品”開發時的特性。包括靜態Receiver、Task-Affinity坑位、自定義Theme、進程坑位、AppCompat、DataBinding等
- 易于集成:無論插件還是主程序,只需“數行”就能完成接入
- 管理成熟:擁有成熟穩定的“插件管理方案”,支持插件安裝、升級、卸載、版本管理,甚至包括進程通訊、協議版本、安全校驗等
- 數億支撐:有360手機衛士龐大的數億用戶做支撐,三年多的殘酷驗證,確保App用到的方案是最穩定、最適合使用的
截止2017年6月底,RePlugin的:
特性 | 描述 |
---|---|
插件數 | 103(核心57個) |
插件占應用比 | 高達83% |
年發版次數 | 高達596次(工作日均2次) |
崩潰率 | 萬分之一(0.01%),極低 |
時間 | 2014年應用,3年驗證 |
目前360公司幾乎所有的億級用戶量的APP,以及多款主流第三方APP,都采用了RePlugin方案。
有關RePlugin的詳細介紹,請點擊這里閱讀《RePlugin 官方 WiKi》。
特性
特性 | 描述 |
---|---|
組件 | 四大組件(含靜態Receiver) |
升級無需改主程序Manifest | 完美支持 |
Android特性 | 支持近乎所有(包括SO庫等) |
TaskAffinity & 多進程 | 支持(坑位方案)** |
插件類型 | 支持自帶插件(自識別)、外置插件** |
插件間耦合 | 支持Binder、Class Loader、資源等 |
進程間通訊 | 支持同步、異步、Binder、廣播等 |
自定義Theme & AppComat | 支持 |
DataBinding | 支持 |
安全校驗 | 支持 |
資源方案 | 獨立資源 + Context傳遞(相對穩定) |
Android 版本 | API Level 9+ (2.3及以上) |
參考文章
APK動態加載框架(DL)解析
Android插件化從入門到放棄-最強合集
包建強的無線技術空間,寫給Android App 開發人員看的 Android 底層知識 置頂8篇
有關Android插件化思考
Android插件化原理解析
Android插件化:從入門到放棄
Android博客周刊專題之-插件化開發