其中,**修復又分為:類載入方案、底層替換方案、instant run 方案。
本篇關於**修復的類載入方案的筆記整理。
涉及原始碼版本為 android 7.1.1。
參考文章:
1、android熱更新實現原理**
2、《android 高階解密》
1、理論基礎
類載入方案是基於 dex 分包方案的。
dex 分包方案主要做的是在打包的時候將應用**分成多個 dex,將應用啟動時必須用到的類和這些類的直接引用類放到主 dex 中,其他**放到次 dex 中。當應用啟動時先載入主 dex,等到應用啟動後再動態載入次 dex。
而關於類的載入,就是遍歷所有的 dex 檔案,從中去載入目標類。這裡就涉及到了乙個類dexpathlist
。
更進一步的,是具體的是,則是涉及到類載入器。
對於類的載入,是通過classloader
來進行的,基於雙親委託模式,會先通過具體的載入器的父載入器來載入類,如果父載入器沒載入到,則會呼叫自身的findclass()
方法來自行載入。
涉及到的載入器包括dexclassloader
和pathclassloader
。
dexclassloader
可以載入 dex 檔案以及包含 dex 的壓縮檔案(apk 和 jar 檔案),而且可以載入指定路徑中的 dex 檔案,包括外部儲存空間的。
pathclassloader
則是 android 系統用來載入系統類和應用程式的類。通常用來載入已經安裝的 apk 的 dex 檔案(安裝的 apk 的 dex 檔案會存在/data/dalvik-cache
中)。
上述兩個 classloader 都繼承自basedexclassloader
。
public
basedexclassloader
(string dexpath, file optimizeddirectory,
string librarysearchpath, classloader parent)
@override
protected class<
?>
findclass
(string name)
throws classnotfoundexception
return c;
}
可以看到,通過basedexclassloader#findclass()
會進一步呼叫前面說到的dexpathlist
型別的pathlist.findclass()
。
public class findclass
(string name, list
suppressed)}}
if(dexelementssuppressedexceptions != null)
return null;
}
在dexpathlist#findclass()
中,會遍歷dexelements
陣列,該陣列的元素的為dexpathlist.element
型別,而其內部又封裝了dexfile
成員變數,該變數就對應著實際的 dex 檔案,更進一步的說,載入 class 實際上是通過dexfile
來實現的。
因此,類載入方案的實現,就是將補丁 dex 對應的element
插入到應用對應的載入器的pathlist
的dexelements
陣列的靠前位置,從而使得後面同名的 class 不被載入。
2、具體實現
補充兩點:
(1)是關於class_ispreverified
的問題(涉及到 dalvik 虛擬機器),具體參見:android 冷啟動熱修復技術雜談,因此在測試的時候要使用基於 art 虛擬機器的機型,即 android 5.0 及以上版本。
(2)由於 android 9.0 隱藏了部分 api,所以無法實現反射替換對應的dexelements
陣列,因此在測試的要使用 9.0 以下的手機。
關鍵部分**:
public
void
dohotfix
(context context)
throws illegalacces***ception, nosuchfieldexception, classnotfoundexception
// 補丁存放目錄為 /storage/emulated/0/android/data/com.lxbnjupt.hotfixdemo/files/patch
// 注意,這裡的 dexfile 是乙個目錄
file dexfile = context.
getexternalfilesdir
(dex_dir);if
(dexfile == null ||
!dexfile.
exists()
)// 得到 new dexclassloader 時需要的儲存路徑
file odexfile = context.
getdir
(optimize_dex_dir, context.mode_private);if
(!odexfile.
exists()
)// 獲取 /storage/emulated/0/android/data/com.lxbnjupt.hotfixdemo/files/patch
// 目錄下的所有檔案,用於找出裡面的補丁 dex
file[
] listfiles = dexfile.
listfiles()
;if(listfiles == null || listfiles.length ==0)
// 獲取補丁 dex 檔案路徑集合
string dexpath =
getpatchdexpath
(listfiles)
; string odexpath = odexfile.
getabsolutepath()
;// 獲取應用對應的 pathclassloader
pathclassloader pathclassloader =
(pathclassloader) context.
getclassloader()
;// 構建 dexclassloader,用於載入補丁 dex
// dexclassloader 構造方法的四個引數:
// 第二個:解壓的 dex 檔案的儲存路徑,必須是乙個內部儲存路徑
// 第三個:包含 c/c++ 庫的路徑集合,可以為 null
// 第四個:父載入器
dexclassloader dexclassloader =
newdexclassloader
(dexpath, odexpath, null, pathclassloader)
;// 這裡要新 new 乙個 dexclassloader 的原因就是為了借助系統來構建出補丁 dex 對應
// 的 element 元素的陣列,從而插入到應用的 pathclassloader 的
// pathlist.dexelements 中
// 通過反射獲取 pathclassloader 的 element 陣列
object pathelements =
getdexelements
(pathclassloader)
;// 獲取構建的 dexclassloader 的 element 陣列
object dexelements =
getdexelements
(dexclassloader)
;// 合併 element 陣列
object combineelementarray =
combineelementarray
(pathelements, dexelements)
;// 通過反射,將合併後的 element 陣列賦值給 pathclassloader 中 pathlist 裡面的
// dexelements 變數
setdexelements
(pathclassloader, combineelementarray)
;}
而且重啟應用之後,如果沒有觸發載入補丁類,則應用還是會載入原來的 bug 類。
Android 熱修復思路整理
流行的熱修復方式 按技術特點劃分 本文採用的熱修復 思路 載入應用程式的classloder classloader classloader context.getclassloader for file file loadeddex 因為系統通過dexclassloder來載入dex,所以需要將新...
iOS實現熱修復的幾種方案
最近,在調研熱修復技術,也稱作熱更新技術。由於蘋果審核週期有時候比較長,這是公司無法忍受的,所以熱修復技術應運而生。經過查閱多方面的資料,進行如下總結,希望對大家有所幫助。現在比較流行的熱修復技術 一 使用jspatch進行熱修復。jspatch能做到通過js呼叫和改寫oc方法。最根本的原因是 ob...
記錄 動態載入dex實現某些熱修復
日記。private boolean loaddex catch exception e return true catch exception e return false 2 建立乙個專案,以org.jemen.test為包名,建立mydex類,想要熱更新的方法名包含 jemen export出...