阿里電子書《深刻探索Android熱修復技術原理》整理的筆記java
1.熱修復技術介紹
- 代碼修復兩大主要方案
- 底層替換方案:限制較多,但時效性好,當即見效
- 類加載方案時效性差,須要從新冷啓動才能見效,但限制少
- 代碼修復底層替換方案
- 底層替換方案是在已經加載了的類中直接替換掉原有方法.
- 不能對原有類進行方法和字段的增減,由於這樣將破壞原有類的結構.
- 方法增減將致使這個類及整個Dex方法數的變化,伴隨着方法索引的變化,這樣訪問方法時就沒法正常的索引到正確的方法;
- 字段增長或減小,全部字段的索引都會發生變化;
- 傳統底層替換方案,都是直接依賴修改虛擬機方法實體中的具體字段.依據的是Android開源版本.若是廠商修改了虛擬機方法實體,替換機制就可能出問題;
- 代碼修復類加載方案
- 類加載方案的原理是在app從新啓動後讓Classloader去加載新的類.
- 在app運行到一半的時候,全部須要發生變動的類都已經被加載過,Android沒法對一個類進行卸載.若是不重啓,原來的類還在虛擬機中,就沒法加載新類.
- 只有在下次重啓時候,在尚未走到業務邏輯前搶先加載補丁中的新類,後續訪問纔是新的類.
- dex比較的最佳粒度,應該是在類的粒度
- Sophix採用的也是全量合成dex的技術.能夠看作是dex文件級別的類插裝方案.Sophix對舊包與補丁包中classes.dex的順序進行了打破與重組,使得系統能夠天然地識別到這個順序,以實現類覆蓋的目的
- 資源修復
- 市面上不少資源熱修復方案都採用了Instant Run的實現
- Instant Run中資源修復步驟:
- 構造一個新的AssetManager,經過反射調用addAssetPath,把這個完整的新資源包加到AssetManager中.這樣就獲得一個含有全部新資源的AssetManager.
- 找到全部以前引用到AssetManager的地方,經過反射,將引用出替換爲AssetManager.
- Sophix資源熱修復沒有采用Instant Run的技術,而是構造了一個package id 爲 0x66 的資源包,這個包裏只包含改變了的資源項,而後直接在原有AssetManager中addAssetPath這個包便可.無需變動AssetManager對象的引用.
- Sophix構造的補丁包的 package id 爲0x66,不與已經加載的 0x7f衝突,因此直接加入到已有的AssetManager中能夠直接使用.
- Sophix資源補丁包中,只包含新增資源,以及原有內容發生了改變的資源.
- SO庫修復:本質上是對native方法的修復和替換
Sophix採用的是相似類修復反射注入方式,把補丁so庫的路徑插入到nativeLibraryDirectories數組的最前面, 這樣加載so庫的時候就是補丁so庫而不是原來的so庫
android
2.代碼熱修復技術
- 底層熱替換原理
- Android的java運行環境,在4.4如下用的是dalvik虛擬機,4.4以上是art虛擬機.
- 在各類Android熱修復方案中,Andfix即時生效.Andfix採用的方法是,在已經加載了的類中直接在native層替換掉全部方法,是在原來的類的基礎上進行修改的.
- 以art,Android6.0爲例,每個Java方法在art中都對應着一個ArtMethod對象,ArtMethod記錄了這個Java方法的全部信息,包括所屬類,訪問權限,代碼執行地址等.
- Andfix會將一箇舊Java方法對應的ArtMethod實例中的全部字段值替換爲新方法的值,這樣全部執行到舊方法的地方,都會取得新方法的執行入口,所屬class,方法索引,所屬dex.像調用舊方法同樣執行了新方法的邏輯.
- 底層熱替換兼容性根源
- 市面上幾乎全部的native替換替換,都是寫死了ArtMethod結構體
- 寫死的ArtMethod結構和Android開源版本中徹底一致,但各個廠家能夠對ArtMethod進行修改,那麼在修改過的設備上,市面上的native替換方案(將方案中寫死了的ArtMethod關聯的新方法的屬性賦值到設備中的ArtMethod實例)就會出現問題,由於兩個ArtMethod中相同字段的索引不一樣
- 突破底層熱替換兼容問題
- native層面替換,實質是替換ArtMethod實例的全部字段.
- 只要把ArtMethod做爲總體進行替換,便可解決兼容問題.
- ArtMethod實例之間,是緊密線形排列的,因此一個ArtMethod的大小,就是其相鄰的兩個方法對應的ArtMethod實例的起始地址的差值.
- 包括Sophix在內的底層替換方案,都只能支持方法的替換,不支持補丁類中增減方法和字段
- 補丁類中增減方法,會致使這個類及整個dex方法數的變化,方法數的變化伴隨方法索引的變化,這樣在調用方法時沒法正常的所引導正確的方法.
- 補丁類中增減字段,也會致使全部字段的索引起生變化.
- 你說不知的Java
- 內部類在編譯期會被編譯爲根外部類同樣的頂級類;
- 非靜態內部類持有外部類的引用,靜態內部類不持有外部類的引用.因此android性能優化中建議自定義Handler的實現儘可能使用靜態內部類,防止外部類Activity類不能被回收致使內存泄漏. 自定義Handler使用靜態內部類避免內存泄漏
- 內部類和外部類之間,訪問彼此的private屬性及方法,編譯期間:
- 外部類訪問內部類的私有成員及方法,編譯期間自動爲內部類生成access&**方法
- 內部類訪問外部類的private屬性及方法,編譯期間也會生成access&**方法提供給內部類
- 同一個類及其內部類,若是老代碼沒有訪問對方的私有屬性/方法,新代碼有訪問對方的私有屬性/方法,若是不能避免生成access&的生成,就會致使方法數的變化,致使熱修復失敗.避免生成access&方法須要:
- 外部類全部的屬性及方法改成public或protected;
- 內部類全部的屬性及方法改成public或protected;
- 在編譯期間,根據匿名內部類在外部類中出現的前後順序,匿名內部類的名稱依次累加:外部類名稱&數字
- 外部類名稱是OutClass,其中對應的內部類在編譯期間的名稱依次是:OutClass&1,OutClass&2,-----
- 爲了實現熱修復,外部類應該極力避免新增及減小匿名內部類;
- 除非是新增匿名內部類到外部類的尾部,不會影響以前添加過的匿名內部類的名稱,否則會致使熱修復失效;
- Java原始類型:double、float、byte、short、int、long、char、boolean
- 若是一個常量的類型是Java原始類型,或String,爲了優化性能,應該用static final修飾;
- static final 引用類型,沒有任何優化效果.
- 由於 static final 修飾的原始類型及String常量,是在所屬類的初始化時賦值,直接在內存中讀取;
- 而 static final 引用類型常量,初始化是在clinit方法中,本質上是經過sget-object指令去獲取值,從虛擬機運行性能上無任何優化;
- 市面上的冷啓動類加載實現方案
- 1:採用dex插樁的方式,單獨放一個幫助類在獨立的dex中讓其餘類調用.最後加載補丁dex獲得dexFile對象,將dexFile做爲參數構建一個Element對象插入到dexElements數組最前面
- 2:提供dex差量包,總體替換dex的方案:差量patch.dex和應用的classes.dex合成完整dex.加載完整dex獲得dexFile對象,做爲參數構建一個Element對象,而後總體替換掉舊的dexElements數組
- 1的缺點是:Dalvik下影響類加載性能,Art下類地址寫死,致使必須包含父類及引用,補丁包很大
- 2的缺點是:dex的合併,內存消耗在 vm heap 上,容易致使OOM,合併失敗
- Sophix採用的代碼修復冷啓動方案
- Dalvik下使用全量Dex方案;
- Art下本質上虛擬機已經支持多dex的加載,只要把補丁dex做爲主dex(classes)便可
- Sophix在Dalvik下全量Dex方案思路
- 基線包dex裏面,去掉補丁包dex中包含的class;這樣補丁+去除了補丁中包含類的基線包,就等於新app中全部類;
- Sophix並無把某個class的全部信息從基線dex中移除,僅僅移除了定義的入口,讓解析基線dex時候找不到這個class的定義便可;這樣不會致使dex的各個部分都發生變化,防止大量調整offset.
- 只要把全部的dex都load進去,單個dex中不存在的類就能夠在運行期間在其餘dex中找到.補丁中的類和基線中的類能夠互相訪問到
3.資源熱修復技術
Android資源的熱修復,就是在app不從新安裝的狀況下,利用下發的補丁包直接更新app中的資源
Sophix的資源熱修復方案數組
1:構造一個 package id 爲0x66的資源包,這個包裏只含有變動的資源,以及新增資源;
2:而後直接在原有AssetManager上調用addAssetPath加入這個資源包便可;
由於咱們補丁包的id和已經加載的0x7f衝突,因此直接加入原有AssetManager便可直接使用
複製代碼
4.SO庫熱修復技術
無性能優化