Android Tinker 熱修復原理

熱修復流派

基於Multidex的Dex注入

代表:TInker,手機QQ空間、Nuwa
原理:將補丁Dex對象的DexFile對象注入到系統ClassLoader相關聯的DexPathList對象的dexElements數組的最前面

Native層方法替換

代表:AndFix,阿里百川HotFix
原理:在Native層對方法的整體數據結構(Method/ArtMethod)進行替換

ClassLoader Hack

代表:Instant Run
原理:基於雙親委派機制,用自定義的IncrementalClassLoader加載補丁Dex,同時將該類加載器設置爲系統類加載器的父加載器

Tinker 熱修復Dex插樁原理

ClassLoader的加載路徑可以包含多個dex文件,每個dex文件關聯一個Element,多個dex文件排列成一個有序的數組dexElements,當查找類的時候會按順序遍歷dex文件,然後從當前遍歷的dex文件中找類,如果找到則返回,如果找不到從下一個dex文件繼續查找。理論上如果在不同的dex中有相同的類存在,那麼優先選擇排在前面的dex文件的類。
在這裏插入圖片描述

源碼分析

我們解壓一個apk,可以看到有這些.dex分包。
在這裏插入圖片描述
對應在代碼裏,BaseDexClassLoader中的pathList就是這些分包。

類加載器 ClassLoader
子類:BaseDexClassLoader

在這裏插入圖片描述

findClass是來尋找指定的類,我們可以看到,會通過pathList來尋找類。
在這裏插入圖片描述
pathList的類型是DexPathList ,我們再來看DexPathList,可以看到有一個dexElements屬性,是Dex的集合。
在這裏插入圖片描述
再來看DexPathList的構造方法
在這裏插入圖片描述
可以看到,通過makePathElements方法,對dexElements進行賦值。
來看makePathElements方法

private static Element[] makePathElements(List<File> files, File optimizedDirectory,
                                          List<IOException> suppressedExceptions) {
    List<Element> elements = new ArrayList<>();
    
    //遍歷文件
    for (File file : files) {
        File zip = null;
        File dir = new File("");
        DexFile dex = null;
        String path = file.getPath();
        String name = file.getName();

        if (path.contains(zipSeparator)) {
            String split[] = path.split(zipSeparator, 2);
            zip = new File(split[0]);
            dir = new File(split[1]);
        } else if (file.isDirectory()) {
            elements.add(new Element(file, true, null, null));
        } else if (file.isFile()) {
        	//如果文件名包含 .dex 後綴
            if (name.endsWith(DEX_SUFFIX)) {
                try {
                	//嘗試加載Dex文件
                    dex = loadDexFile(file, optimizedDirectory);
                } catch (IOException ex) {
                    System.logE("Unable to load dex file: " + file, ex);
                }
            } else {
                zip = file;
                try {
                    dex = loadDexFile(file, optimizedDirectory);
                } catch (IOException suppressed) {
                    suppressedExceptions.add(suppressed);
                }
            }
        } else {
            System.logW("ClassLoader referenced unknown path: " + file);
        }

        if ((zip != null) || (dex != null)) {
        	//將dex文件傳入Element中,並將element添加到elements集合中
            elements.add(new Element(dir, false, zip, dex));
        }
    }

  	return elements.toArray(new Element[elements.size()]);
}

可以看到,這裏會遍歷目錄下的所有文件,然後,如果後綴匹配.dex,則嘗試加載dex,如果加載成功,就將dex添加到elements集合中。
最終,將elements轉爲數組,並返回。

App安裝

然後我們知道,App安裝成功的時候,會複製一份apk文件到應用的私有目錄
/data/app/packageName~1/base.apk

所以可以有思路:將修復好的x.dex下載到本機,插樁到dex集合的最前面,優先加載當中修復好的java類。

實現步驟
  1. 自己創建一個類加載器
  2. 用自己創建的類加載器加載 x.dex 修復包 (得到對於的DexElements[])
  3. 將得到dexElements和系統的dexElements[]進行合併,並且將自有的數據放置在最前面
  4. 通過反射的技術,將合成後的新數組,賦值給系統的pathList
    在這裏插入圖片描述
修改後的java代碼,如何生成dex ?
  1. 通過javac指令編譯成class文件
  2. 通過dx.bat轉成dex文件 (修復包)

核心步驟總結

1.獲取系統ClassLoader的pathList對象

Object pathList = Reflect.on(loader).field("pathList").get();

2.調用makePathElements構造補丁的dexElements

ArrayList<IOException> suppressedExceptions = new ArrayList<>();
Object[] patchDexElements = makePathElements(pathList,extraDexFiles,FileUtil.getDexOptDir(context),suppressedExceptions);

3.將補丁Dex注入系統ClassLoader的pathList對象的dexElements的最前面

expandElementsArray(pathList,patchDexElements);

小結

以上就是QQ空間超級補丁技術,Tinker和QQ空間超級補丁技術的原理基本相同,但對部分缺點進行了優化。

  • 爲了實現修復這個過程,在應用中必須多出一個專門用作修復用的patch.dex,如果修復的類到了一定數量,就需要花不少的時間加載。
  • 在ART模式下,如果類修改了結構,就會出現內存錯亂的問題。爲了解決這個問題,就必須把所有相關的調用類、父類子類等等全部加載到patch.dex中,導致補丁包異常的大,進一步增加應用啓動加載的時候,耗時更加嚴重。

Tinker針對QQ空間超級補丁技術的不足提出了一個提供DEX差量包,整體替換DEX的方案。主要的原理是與QQ空間超級補丁技術基本相同,區別在於不再將patch.dex增加到elements數組中,而是差量的方式給出patch.dex,然後將patch.dex與應用的classes.dex合併,然後整體替換掉舊的DEX文件,以達到修復的目的。

具體詳見阿里最新熱修復Sophix與QQ超級補丁和Tinker的實現與總結

類校驗異常的規避

在這裏插入圖片描述
dex插裝可能會導致該異常,所以需要破壞這3個條件的其中一個。對於Tinker,就是破壞第三個條件,就是通過全量後的方式,以避免打上預校驗標識的類,直接使類不在同一個dex中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章