Android熱修復技術原理及熱修復集成

一 .熱修復原理

       最近一段時間因爲需求變化較大,覺得發版比較麻煩,就瞭解了一下熱修復技術。它更多適用於剛發出去的包有Bug需要緊急修復的時候會用到。即以修復Bug的角度出發,在不需要二次安裝下修復已知的Bug。

瞭解完熱修復的應用場景後就得了解它的原理。首先要認識幾個關鍵的詞。

ClassLoader:用於Android中類的加載。

PathClassLoader:只能加載已經安裝到Android系統中的apk文件(/data/app目錄),是Android默認使用的類加載器。

DexClassLoader:可以加載任意目錄下的dex/jar/apk/zip文件,也就是我們一開始提到的補丁。

這兩個類都是繼承自BaseDexClassLoader。那追根溯源就要看BaseDexClassLoader是如何實現的。

public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }

 代碼中初始化了一個DexPathList對象,所以整個修復過程就已經形成:

1.DexPathList的構造函數,就是將參數中傳遞進來的程序文件(就是補丁文件)封裝成Element對象,並將這些對象添加到一個Element的數組集合dexElements中去。

2.ClassLoaer 的加載機制是雙親委託機制,在這種機制下,一個Class只會被加載一次。將一個具體的類(class)加載到內存中其實是由虛擬機完成的通過DexClassLoader查找一個類,最終就是就是在一個數組中查找特定值的操作。

3.當代碼中的某一個類或者是某幾個類有bug,那麼我們可以在修復完bug之後,可以將這些個類打包成一個補丁文件,然後通過這個補丁文件封裝出一個Element對象,並且將這個Element對象插到原有dexElements數組的最前端,這樣當DexClassLoader去加載類時,優先會從我們插入的這個Element中找到相應的類,雖然那個有bug的類還存在於數組中後面的Element中,但由於雙親加載機制的特點,這個有bug的類已經沒有機會被加載了,這樣一個bug就在沒有重新安裝應用的情況下修復了。

一個完整簡單的修復流程就形成了。當然完成之後就是一個簡單的熱修復框架了。總結成流程圖如下:

附:如需動態修復Bug則需要將補丁文件classes2.dex放在SD目錄下。

二.熱修復框架比較

先放張圖比較一下用的比較多的幾個熱修復框架:

再者是阿里系的方案對比以及:

下面來介紹幾個主要用到的熱修復方案:

QQ空間超級補丁技術(dex插樁方案):類修復

實現原理:超級補丁技術基於DEX分包方案,使用了多DEX加載的原理,大致的過程就是:把BUG方法修復以後,放到一個單獨的DEX裏,插入到dexElements數組的最前面,讓虛擬機去加載修復完後的方法。流程如下:

優勢:

  1. 沒有合成整包(和微信Tinker比起來),產物比較小,比較靈活
  2. 可以實現類替換,兼容性高。(某些三星手機不起作用)

不足:

  1. 不支持即時生效,必須通過重啓才能生效。
  2.  爲了實現修復這個過程,必須在應用中加入兩個dex!dalvikhack.dex中只有一個類,對性能影響不大,但是對於patch.dex來說,修復的類到了一定數量,就需要花不少的時間加載。對手淘這種航母級應用來說,啓動耗時增加2s以上是不能夠接受的事。
  3.  在ART模式下,如果類修改了結構,就會出現內存錯亂的問題。爲了解決這個問題,就必須把所有相關的調用類、父類子類等等全部加載到patch.dex中,導致補丁包異常的大,進一步增加應用啓動加載的時候,耗時更加嚴重。

 Andfix:底層替換方案

實現原理:底層替換方案是在已經加載了的類中直接替換掉原有方法,是在原來類的基礎上進行修改的。因而無法實現對與原有類進行方法和字段的增減,因爲這樣將破壞原有類的結構。在Native修改Filed指針的方式,實現方法的替換,達到即時生效無需重啓,對應用無性能消耗的目的。其依賴虛擬機進行修改,而AndFix裏的ArtMethod是寫死的,廠商改掉則會出現問題。

對於實現方法的替換,需要在Native層操作,經過三個步驟:

優勢:

  1. BUG修復的即時性
  2. 補丁包同樣採用差量技術,生成的PATCH體積小
  3. 對應用無侵入,幾乎無性能損耗

不足:

  1. 不支持新增字段,以及修改<init>方法,也不支持對資源的替換。
  2. 由於廠商的自定義ROM,對少數機型暫不支持。

 

Tinker:類加載

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

而其實現的流程圖如下:

優勢:

  1. 合成整包,不用在構造函數插入代碼,防止verify,verify和opt在編譯期間就已經完成,不會在運行期間進行。
  2. 性能提高。兼容性和穩定性比較高。
  3. 開發者透明,不需要對包進行額外處理。

不足:

  1. 與超級補丁技術一樣,不支持即時生效,必須通過重啓應用的方式才能生效。
  2.  需要給應用開啓新的進程才能進行合併,並且很容易因爲內存消耗等原因合併失敗。
  3.  合併時佔用額外磁盤空間,對於多DEX的應用來說,如果修改了多個DEX文件,就需要下發多個patch.dex與對應的classes.dex進行合併操作時這種情況會更嚴重,因此合併過程的失敗率也會更高。

 

Sophix(推薦):(類加載+底層替換)

這是我比較推薦的熱修復方案,也是我目前項目裏用到的。

實現原理:單純小修改使用的是底層修改方案,代碼超過底層替換限制,則使用類加載替換,而且還使用的是補丁包差量技術,可以使patch體積更小。而so庫的修復本質上是對native方法的修復和替換。詳細說明即是:補丁生成階段,補丁工具會根據實際代碼變動情況進行自動選擇,針對小修改,在底層替換方案限制範圍內的,就直接採用底層替換修復嗎,這樣可以做到代碼修復即時生效。而對於代碼修改超出底層替換限制的,會使用類加載替換,這樣雖然及時性沒那麼好,但最後也可以達到熱修復的目的。另外,運行時階段,Sophix還會再判斷所運行的機型是否支持熱修復,這樣即使補丁支持熱修復,但由於機型底層虛擬機構造不支持,還是會走類加載修復,從而達到最好的兼容性。

實現的過程如下圖所示:

優勢:

  1. 補丁即時生效,不需要應用重啓;
  2. 補丁包同樣採用差量技術,生成的PATCH體積小;
  3. 對應用無侵入,幾乎無性能損耗;
  4. 修改AssetManager的引用處,替換更快更完全。(對比Instanat Run以及所有copycat的實現)
  5. 不必下發完整包,補丁包中只包含有變動的資源。(對比Instanat Run、Amigo等方式的實現)
  6. 不需要在運行時合成完整包。不佔用運行時計算和內存資源。(對比Tinker的實現)

不足:

    沒有開源而且用戶量大了之後開始收費。

 

以上就是對於熱修復原理的實現和方案的介紹,希望對大家學習熱修復有一定的幫助。

 

 

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