1. 程式人生 > >《深入探索Android熱修復原理》程式碼熱修復總結

《深入探索Android熱修復原理》程式碼熱修復總結

阿里巴巴對熱修復技術的發展路線:
1、基於Xposed而來的Dalvik下java method hook技術-Dexposed框架,僅限於Dalvik虛擬機器
2、相容到Art虛擬機器的Andfix,同樣是基於底層的結構替換方案
3、進而發展就是hotfix,基於Andfix,有所提高,但都沒有對資源和so實現修復能力
4、接下來就是這篇主角:17年6月提出的新方案-非入侵時Sophix

程式碼熱修復原理

1、Dalvik下
採用了AndFix中的熱修復方法

extern void __attribute__ ((visibility ("hidden"))) dalvik_replaceMethod(
        JNIEnv*
env, jobject src, jobject dest) { jobject clazz = env->CallObjectMethod(dest, jClassMethod); ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef_fnPtr( dvmThreadSelf_fnPtr(), clazz); clz->status = CLASS_INITIALIZED; Method* meth = (Method*) env->FromReflectedMethod(src); Method*
target = (Method*) env->FromReflectedMethod(dest); LOGD("dalvikMethod: %s", meth->name); meth->clazz = target->clazz; meth->accessFlags |= ACC_PUBLIC; meth->methodIndex = target->methodIndex; meth->jniArgInfo = target->jniArgInfo; meth->registersSize =
target->registersSize; meth->outsSize = target->outsSize; meth->insSize = target->insSize; meth->prototype = target->prototype; meth->insns = target->insns; meth->nativeFunc = target->nativeFunc; }

直接把java method替換成native method
詳細參考:https://www.cnblogs.com/soaringEveryday/p/5338214.html
2、Art下
優化了AndFix下Art虛擬機器下native 替換method的方式,通過整體替換ArtMethod而不是替換ArtMethod內部成員變數來達到整體替換,而不會出現因內部結構改變而出現不相容的情況。如原著作中圖示如下:
這裡寫圖片描述

3、熱修復方式即時生效侷限

1)存在以下兩種情況下不能使用

1、類結構發生了變化,包括類的成員函式或成員變數發現了增加和減少情況,均不適用
2、非靜態方法在熱修復反射呼叫時會出現新舊類校驗導致不適用,只能通過冷啟動類替換方式

2)如下情況會導致發生類結構變化
1、內部類編譯
2、匿名內部類編譯
3、靜態域和程式碼塊編譯和final static域編譯
4、應用混淆導致的方法內聯和剪裁
5、switch-case語句編譯
6、泛型編譯-邊際擦除和型別轉換
……

冷啟動類載入原理

目前主流中大部分都是採用插樁方式-把補丁dex檔案通過DexFile.load載入成DexFile物件,作為一個Element插入pathlist中第一位,但是這種方式存在兩個問題:

一個是ClASS_ISPREVERIFIED:簡單說就是出現了兩個相同的類在兩個dex檔案中,一個補丁dex,一個原dex,原dex在dexopt/dexoat中verify時引用都是在相同dex中而打上這個標籤。
另一個是載入時效能問題:正常情況下在安裝時候會在dexopt轉為成odex時候進行類的Verify和Optimize操作,而插樁方式都繞過了,從而在類載入初始化時候,會重新因為類沒有被打上CLASS_ISPREVERIFIED和CLASS_ISOPTIMIED標籤而verify和optimize,這時候如果大量的類會導致阻塞主執行緒。如原著中圖示
這裡寫圖片描述

1、Art下方案

在Art虛擬機器下已經支援了載入壓縮檔案中的多個dex檔案,而dalvik僅從壓縮檔案中載入classes.dex檔案,Art下優先載入classes.dex,然後載入classes2.dex….,故直接把補丁dex檔案命名為classes.dex,原apk中的dex依次重新命名為classes2.dex…,然後一起壓縮,並通過DexFile.load成DexFile物件,並完成替換dexElements(DexPathlist的成員變數)舊物件,而不是插入到陣列的首位。
其中在DexFile.load載入dex檔案到native記憶體之前,會先找對應的odex檔案,如果沒有,則dalvik下會進行dexopt,Art下會進行dexoat,最後得到一個優化後的odex檔案,虛擬機器執行的都是odex而不是dex。由於轉換成odex過程耗時,阻塞主執行緒,故原著中建議另起一個執行緒來轉為,若被中斷,則刪除odex。

2、dalvik下全量dex方案

由於dalvik下不具備多dex檔案載入,原著提出了一個類似微信Tinker的合成全量dex的方案。由於Tinker中生成補丁dex和合成新dex過於精細,補丁dex是由新老dex比較指令等維度來生成的,而本書中通過在類的維度差役生成補丁,再把原dex中相同的類去掉,再合成一個完整的dex,把補丁插樁到dexElements(DexPathlist的成員變數)陣列的第一位,這樣先從補丁找類,沒有再從原dex中找。其中去掉原dex中的類方法,Sophix提出一種方式如下圖:
這裡寫圖片描述
僅僅去掉dex檔案中的類的定義,而不是去掉類的實體,這樣會大大提搞操作效率和安全性。

3、存在的限制

dvmOptimizeClass優化導致的影響:優化會導致重寫虛擬機器指令-invoke-virtual 改成invoke-virtual-quick,後面的索引是每個類的vtable中索引值,而這個vtable索引值是按照類中先後順序而來,就會出現第一位方法錯亂。故增加方法都是在類最後增加