主流插件式框架探究

前言
目前移動(dòng)端產(chǎn)品功能越來(lái)越復(fù)雜错森,模塊不斷增加峻仇,APK體積也不斷增長(zhǎng)俄讹。由于Android Dalvik最初設(shè)計(jì)的問(wèn)題哆致,單個(gè).dex文件方法數(shù)最多是65536個(gè)。因此患膛,APK難免會(huì)遇到64K方法數(shù)限制的問(wèn)題沽瞭。
Google官方提供了MultiDex解決方案。但是該解決方案剩瓶,有比較大的局限性。

應(yīng)用進(jìn)程啟動(dòng)時(shí)MultiDex需要在主線程去做DEXOPT操作城丧,中間涉及到文件讀寫延曙、文件驗(yàn)證、數(shù)據(jù)復(fù)制亡哄、反射調(diào)用等枝缔,非常耗時(shí),應(yīng)用啟動(dòng)將變得很慢蚊惯,甚至出現(xiàn)ANR愿卸。

由于Dalvik LinearAlloc的BUG,Android 4.0(API level 14)之前的系統(tǒng)截型,可能導(dǎo)致應(yīng)用啟動(dòng)失敗趴荸。應(yīng)用上線前,需要針對(duì)低版本系統(tǒng)做嚴(yán)格測(cè)試宦焦。另外Android 5.0(API level 21)之前的系統(tǒng)发钝,Dalvik加載類的堆內(nèi)存的大小有限制,可能導(dǎo)致使用了MultiDex的應(yīng)用在分配內(nèi)存時(shí)波闹,會(huì)出現(xiàn)崩潰酝豪。

引入MultiDex時(shí),會(huì)存在多個(gè)dex文件精堕,應(yīng)用啟動(dòng)時(shí)孵淘,先加載了主dex文件,其它的dex文件在Application的attachBaseContext()方法中加載歹篓。Android構(gòu)建工具幫我們分析處理了一部分依賴瘫证,將應(yīng)用啟動(dòng)需要的類都放入了主dex文件中。但是項(xiàng)目中我們自己引入的第三方庫(kù)或者底層的native代碼滋捶,可能還有一些類的依賴構(gòu)建工具無(wú)法處理痛悯,會(huì)導(dǎo)致應(yīng)用啟動(dòng)時(shí)找不到相關(guān)類而崩潰。

綜上所述重窟,MultiDex方案并不完善载萌,將直接應(yīng)用啟動(dòng)變慢和崩潰的問(wèn)題。為了解決這些問(wèn)題,我們引入插件的概念扭仁。

技術(shù)關(guān)鍵點(diǎn)
插件框架的基本形式就是將一個(gè)apk中的不同功能模塊進(jìn)行拆分垮衷,打包到不同的dex或者apk文件中,而主工程是一個(gè)提供了加載模塊dex和apk的框架乖坠。插件框架需要著重解決如下幾個(gè)問(wèn)題:

代碼的動(dòng)態(tài)加載搀突。Android系統(tǒng)提供了DexClassLoader,可以用來(lái)動(dòng)態(tài)加載apk熊泵、dex和jar文件。加載順序決定了起作用的類顽分。這一點(diǎn)可做用于熱修復(fù)功能。

四大組件的動(dòng)態(tài)注冊(cè)和生命周期管理卒蘸。通過(guò)插樁的方式做動(dòng)態(tài)代理「孜郑可以在多個(gè)層級(jí)做Hack,不同的層級(jí)Hack時(shí)需要做的適配工作難度也不同趾牧。一般都需要反射替換Instrumentation,并對(duì)一些關(guān)鍵方法使用動(dòng)態(tài)代理武氓,以達(dá)到“欺上瞞下”的目的。

開源框架簡(jiǎn)介
目前县恕,已經(jīng)有多種插件框架的實(shí)現(xiàn)方案,不同的方案都存在各自的優(yōu)缺點(diǎn)忠烛。以下簡(jiǎn)單介紹幾個(gè)主要的插件框架的基本原理和優(yōu)缺點(diǎn)属提。

DroidPlugin
DroidPlugin是360手機(jī)助手實(shí)現(xiàn)的一種插件框架,可以直接加載運(yùn)行第三方的獨(dú)立apk美尸,不需要對(duì)apk進(jìn)行修改或安裝冤议,插件 apk可以獨(dú)立運(yùn)行。其特性主要有以下幾點(diǎn):

通過(guò)Stub插樁的方式师坎,預(yù)先靜態(tài)注冊(cè)多個(gè)不同屬性的Activity恕酸、Service、Receiver和Provider胯陋,動(dòng)態(tài)代理蕊温。一個(gè)插件一個(gè)ClassLoader袱箱,插件之間代碼完全隔離,互不影響义矛。
資源動(dòng)態(tài)加載发笔。一個(gè)插件一個(gè)AssetManager,插件之間資源完全隔離凉翻,互不影響了讨。
Hack了幾乎所有的通過(guò)Context獲取到的系統(tǒng)服務(wù)(AM、PM等)制轰,Hack了和應(yīng)用進(jìn)程密切相關(guān)的Instrucmentation前计、ApplicationThread、ActivityThread等相關(guān)類垃杖〔信冢框架中很大一部分代碼都是與Hack有關(guān)。
不同的插件APK運(yùn)行于不同的進(jìn)程缩滨,互不干擾。插件之間支持aidl進(jìn)程間通信泉瞻。提供進(jìn)程管理機(jī)制脉漏。
存在局限的地方:

由于進(jìn)程隔離的原因,宿主和插件分別使用了不同的ClassLoader和AssetManager袖牙,插件和宿主侧巨、插件和插件之間資源和代碼不能共享。
插件apk中所有的Intent Filter無(wú)法工作鞭达。要啟動(dòng)插件中的四大組件司忱,必須要指定包名和類名。
RemoteView支持不是太好畴蹭,不支持自定義資源的Notification坦仍。
帶有native庫(kù)的插件支持不是太好,可能存在異常崩潰叨襟。
Hack了很多系統(tǒng)類繁扎,兼容性方面可能會(huì)隱患較多。
綜合以上信息糊闽,DroidPlugin比較適合主程序只提供入口,而插件apk對(duì)宿主不存在依賴提澎,插件之間無(wú)太多通信要求的場(chǎng)景盼忌。

Small
Small的核心是輕巧,支持跨平臺(tái)(支持Android絮宁、iOS绍昂、Web)偿荷。其特性主要有以下幾點(diǎn):

輕度Hack跳纳,代碼相對(duì)很少寺庄,邏輯簡(jiǎn)單。插件可以是apk文件赢织、so庫(kù)馍盟、lib庫(kù)贞岭。
提供了Gradle插件輔助構(gòu)建項(xiàng)目,修改各個(gè)插件資源id话速,避免資源id重復(fù)問(wèn)題芯侥。
通過(guò)Stub插樁的方式,預(yù)先靜態(tài)注冊(cè)多個(gè)Activity活合,動(dòng)態(tài)代理物赶。
資源在編譯時(shí)使用自定義gradle插件的方式區(qū)分開酵紫。多個(gè)插件共享進(jìn)程,共享代碼橄唬,共享資源仰楚。同一個(gè)ClassLoader加載所有插件。
使用uri的方式啟動(dòng)插件侨嘀、傳遞參數(shù)咬腕,比較靈活葬荷。
基于Apache協(xié)議開源。
插件配置可以存放在服務(wù)端煞赢,demo中提供了簡(jiǎn)單的版本管理、在線更新機(jī)制吹截。
其主要問(wèn)題也比較明顯:

目前不支持懶加載模式波俄。所有插件都是在應(yīng)用進(jìn)程啟動(dòng)時(shí)一次性加載的,內(nèi)存占用較大捉貌。如果要支持按需加載趁窃,需要自己做改進(jìn)急前。
除了Activity裆针,其它三大組件不支持插件化。作者沒(méi)有支持其它三大組件的意向澡刹。插件中所有的Intent Filter無(wú)法工作罢浇。
插件更新后不是實(shí)時(shí)生效,而是在類被重新加載的時(shí)候生效奏甫。生效時(shí)間和類加載時(shí)機(jī)一致阵子。
Small核心類代碼凌亂胜蛉,部分代碼嚴(yán)重影響程序穩(wěn)定性誊册,還附帶了部分業(yè)務(wù)邏輯案怯,擴(kuò)展性差。
綜合以上信息嘲碱,Small比較適合插件依賴于主程序麦锯,并且插件和主程序之間存在通信的場(chǎng)景,并且程序不宜規(guī)模太大鹅巍。

DynamicAPK
DynamicAPK是攜程的開源項(xiàng)目骆捧,但是目前作者已經(jīng)不再維護(hù)凑懂。其主要特點(diǎn)如下:

同一個(gè)ClassLoader加載所有的插件代碼梧宫,通過(guò)反射調(diào)整加載順序,進(jìn)而實(shí)現(xiàn)了熱修復(fù)功能巷帝。一次性加載所有插件扫夜,未實(shí)現(xiàn)按需加載笤闯。
所有的插件資源都加載到了一個(gè)AssetManager中颗味,并通過(guò)反射動(dòng)態(tài)替換了Context獲取的Resource。
Activity等組件需要在宿主程序中預(yù)先注冊(cè)时呀。
直接改造aapt的源碼谨娜,通過(guò)動(dòng)態(tài)配置的方式確定插件資源id的PP字段趴梢,避免id重復(fù)坞靶。
ACDD
ACDD的原名是OpenAtlas,目前作者也已經(jīng)不再維護(hù)辫封。攜程的DynamicAPK有相當(dāng)一部分代碼借鑒了此項(xiàng)目廉丽。其主要特點(diǎn)如下:

自定義ClassLoader欣福,擴(kuò)展了雙親委派機(jī)制,用于加載插件代碼拓劝。不同插件使用不同的ClassLoader,實(shí)現(xiàn)了代碼的隔離栖博,也實(shí)現(xiàn)了通用代碼的共享仇让。使用額外的配置文件管理插件和插件之間的依賴關(guān)系丧叽,實(shí)現(xiàn)了懶加載機(jī)制踊淳。
所有的插件資源都加載到了一個(gè)AssetManager中嚣崭,并通過(guò)反射動(dòng)態(tài)替換了Context獲取的Resource雹舀。實(shí)現(xiàn)了懶加載機(jī)制说榆。
Activity等組件需要在宿主程序中預(yù)先注冊(cè)签财。
代碼和相關(guān)文檔中未提及資源id重復(fù)的問(wèn)題是如何修正的唱蒸。其內(nèi)部的build tools則對(duì)所有插件和配置文件做了一個(gè)簡(jiǎn)單md5校驗(yàn)神汹,未發(fā)現(xiàn)資源id相關(guān)的代碼古今。應(yīng)該是使用aapt打包時(shí)屁魏,指定了package id。
DynamicLoadApk
大概算是最早開源的插件化框架了捉腥,github上已經(jīng)不怎么活躍了氓拼。其主要特點(diǎn)如下:

支持插件對(duì)主程序無(wú)調(diào)用、接口調(diào)用、完全調(diào)用3種集成方式桃漾。
支持Activity坏匪、FragmentActivity、Service組件呈队,并且組件不需要在主程序的AndroidManifest中聲明剥槐。
將Activity的生命周期抽象出接口,在代理類里實(shí)現(xiàn)對(duì)Activity生命周期的管理宪摧,并在代理類里保存原Activity的引用(that應(yīng)用)。
DexClassLoader加載插件代碼,AssetManager管理插件資源。插件之間資源和代碼可以做到相互隔離瞧柔。
自定義Intent和API實(shí)現(xiàn)對(duì)插件Activity的啟動(dòng)廉邑。
主要問(wèn)題如下:

強(qiáng)侵入性牵祟,組件內(nèi)很多API需要使用that調(diào)用,并需要繼承特定的類捡需,啟動(dòng)組件需要調(diào)用私有API饰剥。
插件的集成方式一旦確定將不能更改。不同的集成方式,編譯配置也不相同。
插件之間資源和代碼無(wú)法共享。啟動(dòng)插件中的組件必須使用包名+類名的方式钞瀑。
VirtualApp
VirtualApp是一個(gè)App虛擬化引擎(簡(jiǎn)稱VA)。VA在App內(nèi)部創(chuàng)建一個(gè)虛擬空間,App可以在虛擬空間內(nèi)任意安裝、卸載落剪、啟動(dòng)apk抄瑟。而這一切都與外部隔離,如同一個(gè)黑盒猴誊。目前已經(jīng)有較多的應(yīng)用使用了VA项阴,并支持多種方式的應(yīng)用加固歉胶。其主要特性如下:

支持四大組件辫塌,插件中的靜態(tài)注冊(cè)的Receiver會(huì)轉(zhuǎn)為動(dòng)態(tài)注冊(cè)储矩。
插件和插件之間高度隔離只酥,運(yùn)行于獨(dú)立的進(jìn)程赠潦。
插件之間支持aidl通信绷跑,支持Intent隱式調(diào)用垦藏。
IO重定向田绑,插件相關(guān)的文件存放于主程序私有文件目錄下诱咏。
通過(guò)動(dòng)態(tài)代理的方式袋狞,比DroidPlugin Hack了更多的系統(tǒng)類和方法,模擬了一個(gè)更完善的小型系統(tǒng)。
項(xiàng)目比較活躍默责,問(wèn)題的解答和bug修復(fù)相對(duì)較快奇适。
主要缺點(diǎn)如下:

Hack了很多系統(tǒng)類和方法,雖然比DroidPlugin穩(wěn)定愈犹,但還是存在很多兼容性方面的問(wèn)題矮冬。
使用GPL3.0的授權(quán),有開源感染的風(fēng)險(xiǎn)次哈。
插件隔離的原因胎署,不支持代碼和資源的共享。
Atlas
Atlas是阿里在17年3月份才開源的窑滞,目前還處于快速迭代開發(fā)期琼牧,文檔和demo都還不完善,部分細(xì)節(jié)的功能還有不少的BUG葛假。

Atlas主要的特性有以下幾點(diǎn):

單進(jìn)程運(yùn)行,更像一個(gè)組件框架滋恬,包含host聊训、local bundle和remote bundle。
所有bundle的代碼和資源都是動(dòng)態(tài)加載恢氯,并且支持按需加載带斑。
自定義兩個(gè)ClassLoader,一個(gè)實(shí)現(xiàn)類加載時(shí)的路由邏輯勋拟,另一個(gè)則主要負(fù)責(zé)加載bundle中的類和其依賴的bundle中的類勋磕。
自定義ResourceManager,每個(gè)bundle里的資源都加入了同一個(gè)AssetManager敢靡。
支持差分升級(jí)和遠(yuǎn)程bundle調(diào)用挂滓。
提供了打包插件,編譯期自動(dòng)合并Manifest文件啸胧,自動(dòng)分配bundle的資源package id赶站,自動(dòng)生成差分包,并提供單模塊調(diào)試功能纺念。
主要缺點(diǎn)如下:

項(xiàng)目剛開源贝椿,文檔很少,主要功能不穩(wěn)定陷谱。
差分升級(jí)和遠(yuǎn)程bundle調(diào)用烙博,需要強(qiáng)有力的后臺(tái)和測(cè)試支撐。
差分升級(jí)還不支持動(dòng)態(tài)添加4大組件的功能烟逊。


作者:47045039
來(lái)源:CSDN
原文:https://blog.csdn.net/mountains2001/article/details/61196497
版權(quán)聲明:本文為博主原創(chuàng)文章渣窜,轉(zhuǎn)載請(qǐng)附上博文鏈接!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宪躯,一起剝皮案震驚了整個(gè)濱河市图毕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌眷唉,老刑警劉巖予颤,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件囤官,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蛤虐,警方通過(guò)查閱死者的電腦和手機(jī)党饮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)驳庭,“玉大人刑顺,你說(shuō)我怎么就攤上這事∷浅#” “怎么了蹲堂?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)贝淤。 經(jīng)常有香客問(wèn)我柒竞,道長(zhǎng),這世上最難降的妖魔是什么播聪? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任朽基,我火速辦了婚禮,結(jié)果婚禮上离陶,老公的妹妹穿的比我還像新娘稼虎。我一直安慰自己,他們只是感情好招刨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布霎俩。 她就那樣靜靜地躺著,像睡著了一般沉眶。 火紅的嫁衣襯著肌膚如雪茸苇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天沦寂,我揣著相機(jī)與錄音学密,去河邊找鬼。 笑死传藏,一個(gè)胖子當(dāng)著我的面吹牛腻暮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播毯侦,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼哭靖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了侈离?” 一聲冷哼從身側(cè)響起试幽,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎卦碾,沒(méi)想到半個(gè)月后铺坞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體起宽,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年济榨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坯沪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡擒滑,死狀恐怖腐晾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情丐一,我是刑警寧澤藻糖,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站库车,受9級(jí)特大地震影響巨柒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凝颇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一潘拱、第九天 我趴在偏房一處隱蔽的房頂上張望疹鳄。 院中可真熱鬧拧略,春花似錦、人聲如沸瘪弓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)腺怯。三九已至袱饭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間呛占,已是汗流浹背虑乖。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晾虑,地道東北人疹味。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像帜篇,于是被迫代替她去往敵國(guó)和親糙捺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容