深入探索Android熱修復技術原理讀書筆記 —— 熱修復技術介紹


1.1 什么是熱修復

對於廣大的移動開發者而言,發版更新是最為尋常不過的事了。然而,如果你 發現剛發出去的包有緊急的BUG需要修復,那你就必須需要經過下面這樣的流程:

 

這就是傳統的更新流程,步驟十分繁瑣。總的來說,傳統流程存在這幾大弊端:

  • 重新發布版本代價太大

  • 用戶下載安裝成本太高

  • BUG 修復不及時,用戶體驗太差

相應的,許多開發者找到了比較合適的解決辦法。

  1. Hybrid 方案。也就是把需要經常變更的業務邏輯以 H5 的方式獨立出來。而這種方案, 需要傳統的 java 開發者學習前端語言,不僅增加了學習成本,而且還要對原先的邏輯 進行合適的抽象和轉換。並且,對於無法轉為 H5 形式的代碼仍舊是無法修復的。

  2. 使用插件化方案來解決問題,像 Atlas 或者 DroidPlugin 方案。 而這類方式,移植成本非常高,還要學習整套插件化工具,對原先老代碼的改造。

於是,熱修復技術應運而生了。

1.2 技術沉淀

阿里系:

  • Dexposed:基於Xposed改進,針對Android Dalvik虛擬機運行的Java Method Hook技術,但無法兼容Android5.0以后的虛擬機

  • Andfix:也是一種底層替換的方案,做到了 Dalvik 和 ART 的兼容

  • Hotfix:結合實際工程中的使用Andfix的經驗,推出阿里百川Hotfix,但只提供了代碼層面的修復,對於資源和so的修復還未實現

  • Sophix:2017年6月推出Sophix,打破了各家紛爭的局面,在代碼修復,資源修復,so修復方面,都做到了業界領先

其他著名的熱修復,但是各自有各自的局限性,補丁過大,效率低下,不夠穩定,用起來繁瑣:

  • 騰訊 QQ 空間的超級補丁

  • 微信的 Tinker

  • 餓了么的 Amigo

  • 美團的 Robust

1.3 詳細比較

Sophix和Tinker與Amigo的比較:

各項指標都占優,唯一不支持的就是四大組件的修復

1.4 技術概覽

1.4.1 設計理念

Sophix 的設計理念,就是非侵入性

  • 最終的實現只有兩個生成的新舊 apk,唯一要做的就是初始化和請求補丁兩行代碼

  • 不會侵入 apk 的 build 流程中

  • 不改變任何打包組件

  • 不插入任何 AOP 代碼

1.4.2 代碼修復

代碼修復有兩大主要方案,一種是阿里系的底層替換方案,另一種是騰訊系的類加載方案。

兩種方案各有優劣:

  • 底層替換方案限制頗多,但時效性最好,加載輕快,立即見效。

  • 類加載方案時效性差,需要重新冷啟動才能見效,但修復范圍廣,限制少。

底層替換方案

底層替換方案是在已經加載了的類中直接替換掉原有方法,是在原來類的基礎上進行修改的。因而無法實現對與原有類進行方法和字段的增減,因為這樣將破壞原有類的結構。

一旦補丁類中出現了方法的增加和減少,就會導致這個類以及整個 Dex 的方法數的變化。方法數的變化伴隨着方法索引的變化,這樣在訪問方法時就無法正常地索引到正確的方法了。如果字段發生了增加和減少,和方法變化的情況一樣,所有字段 的索引都會發生變化。並且更嚴重的問題是,如果在程序運行中間某個類突然增加了 —個字段,那么對於原先已經產生的這個類的實例,它們還是原來的結構,這是無法改變的。而新方法使用到這些老的實例對象時,訪問新增字段就會產生不可預期 的結果。

這是這類方案的固有限制,而底層替換方案最為人詬病的地方,在於底層替換的不穩定性。

通過對代碼的底層替換原理重新進行了深入思考,從克服其限制和兼容性入 手,以一種更加優雅的替換思路,實現了即時生效的代碼熱修復。

采用一種無視底層具體結構的替換方式,這種方式不僅解決了兼容性問題,並且由於忽略了底層ArtMethod結構的差異,對於所有的Android版本都不 再需要區分,代碼量大大減少。即使以后的Android版本不斷修改ArtMethod 成員,只要保證ArtMethod數組仍是以線性結構排列,就能直接適用於將來的 Android 8.0、9.0等新版本,無需再針對新的系統版本進行適配了。

類加載方案

類加載方案的原理是在 app 重新啟動后讓 Classloader 去加載新的類。因為在 app運行到一半的時候,所有需要發生變更的類已經被加載過了,在 Android 上無法對一個類進行卸載的。如果不重啟,原來的類還在虛擬機中,就無法加載新類。 因此,只有在下次重啟的時候,在還沒走到業務邏輯之前搶先加載補丁中的新類,這樣后續訪問這個類時,就會Resolve為新類。從而達到熱修復的目的。

再來看看騰訊系三大類加載方案的實現原理。

  1. QQ 空間方案會侵入打包流程,並 且為了 hack 添加一些無用的信息,實現起來很不優雅。

  2. QFix 的方案,需要獲取 底層虛擬機的函數,不夠穩定可靠,並且有個比較大的問題是無法新增public函數。

  3. 微信的 Tinker 方案是完整的全量 dex 加載,並且可謂是將補丁合成做到了極致。Tinker 的合成方案,是從 dex 的方法和指令維度進行全量合成,整個過程都是自己研發的。雖然可以很大地節省空間,但由於對dex內容的比較粒度過細,實現較為復雜,性能消耗比較嚴重。實際 上,dex 的大小占整個apk的比例是比較低的,一個 app 里面的dex文件大小並不 是主要部分,而占空間大的主要還是資源文件。因此,Tinker 方案的時空代價轉換的性價比不高。

dex 比較的最佳粒度,應該是在類的維度。它既不像方法和指令維度那樣的細微,也不像 bsbiff 比較那般的粗糙。在類的維度,可以達到時間和空間平衡的最 佳效果。基於這個准則,另辟蹊徑,實現了一種完全不同的全量dex替換方案。

  • 直接利用 Android 原先的類查找和合成機制,快速合成新的全量 dex。么一來,既不需要處理合成時方法數超過的情況,對於 dex 的結構也不用進行破壞性重構。

  • 重新編排了包中dex的順序。虛擬機查找類的時候,會優先找到 classes.dex 中的類,然后才是 classes2.dex、classes3.dex,也可以看做是 dex 文件級別的類插樁方案。這個方式對舊包與補丁包中 classes.dex 的順 序進行了打破與重組,最終使得系統可以自然地識別到這個順序,以實現類覆蓋的目 的。大大減少合成補丁的開銷。

雙劍合璧

既然底層替換方案和類加載方案各有其優點,把他們聯合起來不是最好的選擇嗎?

Sophix的代碼修復體系正是同時涵蓋了這兩種方案。兩種方案的結合,可以實現優勢互補,完全兼顧的作用,可以靈活地根據實際情況自動切換。

在補丁生成階段,補丁工具會根據實際代碼變動情況進行自動選擇,

  • 針對小修改,在底層替換方案限制范圍內的,就直接采用底層替換修復嗎,這樣可以做到代碼修復即時生效。

  • 對於代碼修改超出底層替換限制的,會使用類加載替換,這樣雖然及時性沒那么好,但總歸可以達到熱修復的目的。

  • 運行時階段,Sophix 還會再判斷所運行的機型是否支持熱修復,這樣即使補丁支持熱修復,但由於機型底層虛擬機構造不支持,還是會走類加載修復,從而達到最好的兼容性。

1.4.3 資源修復

目前市面上的很多資源熱修復方案基本上都是參考了 Instant Run 的實現。實際 上,Instant Run 的推出正是推動這次熱修復浪潮的主因,各家熱修復方案,在代碼、 資源等方面的實現,很大程度上地參考了 Instant Run的代碼,而資源修復方案正是 被拿來用到最多的地方。

簡要說來,Instant Run 中的資源熱修復分為兩步:

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

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

新的實現方式:構造了一個 package id 為 0x66 的資源包,這個包里只包含改變了的資源項,然后直接在原有 AssetManager 中 addAssetPath 這個包就可以了。由於補丁包的 package id 為 0x66,不與目前已經加載的 0x7f 沖突,因此直接加入到已有的 AssetManager 中就可以直接使用了。

補丁包里面的資源,只包含原有包里面沒有而新的包里面有的新增資源,以及原有內容發生了改變的資源。並且,我們采用了更加優雅的替換方式,直接在原有的 AssetManager 對象上進行析構和重構,這樣所有原先對 AssetManager對象的引用是沒有發生改變的,所以就不需要像Instant Run 那樣 進行繁瑣的修改了。

可以說,我們的資源修復方案,優越性超過了 Google官方的Instant Run 案。整個資源替換的方案優勢在於:

  • 不修改 AssetManager 的引用處,替換更快更完全。(對比 Instanat Run  及所有 copycat 的實現)

  • 不必下發完整包,補丁包中只包含有變動的資源。(對比 Instanat Runs Amigo 等方式的實現)

  • 不需要在運行時合成完整包。不占用運行時計算和內存資源。(對比 Tinker 實現)

1.4.4 SO庫修復

SO 庫的修復本質上是對 native 方法的修復和替換。

我們采用的是類似類修復反射注入方式。把補丁 so 庫的路徑插入到 nativeLi- braryDirectories 數組的最前面,就能夠達到加載 so 庫的時候是補丁 so 庫,而不是原來 so 庫的目錄,從而達到修復的目的。

采用這種方案,完全由 Sophix 在啟動期間反射注入 patch 中的 so 庫。對開發者依然是透明的。不用像某些其他方案需要手動替換系統的 System.load 來實現替 換目的。

1.5 本章小結

本章介紹了熱修復技術的主要使用場景和為業界帶來的變化。詳細說明了阿里巴巴推出的熱修復解決方案 Sophix 的由來,同時與其他各大主流方案進行了比較。另外,粗略介紹了熱修復所涉及的各個方面,並引導概述后續各個章節。

  

參考文章

深入探索Android熱修復技術原理讀書筆記——第一章:熱修復技術介紹

深入探索Android熱修復技術原理[book]

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM