天天看點

《深入探索Android熱修複技術原理》讀後感以及插件化思考

最近編輯于2019/02/17

主要介紹了阿裡Sophix方案。

熱修複的概念:

AndroidManifest出現BUG是無法修複的,想增加四大元件,可通過預先在安裝包的AndroidManifest裡面埋入代理的元件,在每次新增元件時,通過預埋的代理元件實作與系統程序間的通信。

熱修複需要在更新檔包中包含一個新邏輯的dex檔案。

資源的修複,主要通過修改資源包的内容。

so庫修複是通過在加載時優先加載更新檔包的so庫。

熱替換代碼修複:

Andfix底層替換的局限性:Andfix是根據公開的Android源碼對ArtMethod結構體裡面的字段進行的修改。如果手機廠商修改了ArtMethod的結構體(其實是在結構體前面增加字段,編譯為AOT機器碼後發生替換錯位),就會導緻機型不支援。

而Sophix采用了整體替換ArtMethod的方案,就避免了手機廠商修改結構體的問題。但也帶來需要計算ArtMethod大小的問題,後來發現ArtMethod Array的位址是緊密排列的,通過相鄰兩個ArtMethod的起始位址的內插補點計算出ArtMethod的大小。優點隻要以後ArtMethod數組仍是以線性結構排列,該方案仍能适用。

通路權限的問題:通常是因為類是由不同的Classloader加載導緻的,可以通過反射或者采用冷啟動的方式來解決。

熱替換的局限:因為熱替換需要原有的類執行個體的結構不變。是以使用Sophix進行熱修複,想讓使用者體驗到熱替換,隻能在原有類的方法裡修改或者新增一個原包不存在的新類(PS這樣會使代碼變亂,在上更新包前應該重新整理代碼)。還有其他一些局限:比如盡量不要增加非靜态内部類、需要混淆配置加上-dontoptimize防止方法裁剪與内聯、防止因泛型類型擦除與多态沖突而産生的橋接方法、基本不支援Lambda表達式、更新檔類不能引用非public類。

冷啟動代碼修複:

QQ空間和Tinker的冷啟動方案與前面的熱部署模式不相容,Sophix尋求一種既能無侵入打包又能做熱部署的補充解決方案。QQ空間使用的插樁(需要增加一個含有無關幫助類的dex,原dex中所有類的構造函數都引用這個類)會影響類加載效率。Tinker采用了把原有dex和更新檔包裡的dex重新合并的方案,Sophix則采用去除基線包中的更新檔類再加上更新檔。Tinker對Application的處理是需要開發者将自己的Application替換成TinkerApplication,而Sophix直接再JNI層去除Application類的pre-verified标志(需要Application用到的所有非系統類都和Application位于同一個dex裡,因為Android官方multi-dex機制會自動将Application用到的類都打包到主dex中,是以隻要把熱修複初始化放在attachBaseContext的最前面就行),同時Sophix提供了指定AndroidManifest的Application為SophixStubApplication的方案(會解決一些入口類的問題,同時也不影響使用者通過設定SophixEntry自定義Application)。

Android APP的啟動順序:Application.attachBaseContext——>ContentProvider.onCreate——>Application.onCreate——>Activity.onCreate。

資源熱修複技術:

普遍的做法是參考Instant Run,把資源熱修複分為兩步:

1、構造一個新的AssetManager,并通過反射調用addAssetPath,把這個完整的新資源包加入到AssetManager中。這樣就得到了一個含有所有新資源的AssetManager。

2、找到所有之前引用到原有AssetManager的地方,通過反射,把引用處替換為新的AssetManager。

Sophix采用了另辟蹊徑的方案,主要是使更新檔包的pacakage id為0x66,使其不與已經加載的資源沖突(修改資源時,還需在代碼引用處做出相應修改,因為原先的資源還是存在的)。

so庫熱修複技術:

由于so庫熱部署的種種限制,Sophix采用了反射注入重新開機生效的冷啟動方案。

編輯于2019/02/18

插件化思考

插件化技術類似于熱修複技術,插件化更注重于多個插件的版本控制,也更注重于插件的即插即用(無需冷啟動更新),但也由于不涉及原類的修改使得即插即用成為可能。

插件化也分為so庫的加載、資源的加載、代碼的加載。

so庫的加載使用了System.load或者System.loadLibrary的方案,如同上面那本書中所講,實際是存在一定限制的。

資源的加載也是從參照Instant Run的方案(我的一個無侵入支援應用内以及插件式的安卓應用換膚架構就是采用的這種方案)到修改資源id的技術衍變。

由于無需修改原類,使得代碼的即插即用成為可能。這是插件化着重研究的方面。簡單一點的如同上面熱修複技術提到的預先在安裝包的AndroidManifest裡面埋入代理的元件,在每次新增元件時,通過預埋的代理元件實作與系統程序間的通信。複雜一點主要是運用靜态代理進行Hook,或者動态代理進行Hook(Shawn_Dut 講解了Android 動态代理以及利用動态代理實作 ServiceHook,GitHub上的源碼,隻不過插件化需要Hook的對象很多以及一些其他細節問題需要考慮)。

繼續閱讀