默認(rèn)已知:Android Apk的打包過程。
Tinker熱修復(fù)分三部分:
class文件修復(fù)、資源文件修復(fù)和so文件修復(fù)。
一、class文件修復(fù)(代碼修復(fù))原理
1. ClassLoader源碼解讀,局部的繼承關(guān)系:
ClassLoader->BaseDexClassLoader->DexClassLoader->DexClassLoaderProvider

2. Android Dex文件的加載過程:
我們會使用DexClassLoader去加載dex文件,DexClassLoader會將這個任務(wù)委派給DexPathList中的makeDexElements方法,在makeDexElements中調(diào)用了native層的 c++方法去真正的加載dex文件,然后返回DexFile的對象,通過這個對象構(gòu)建一個Element的對象,然后將這個Element添加到dexElements的數(shù)組中。

3. class文件的查找過程:
DexClassLoader通過findClass去查找一個類,同樣它也是委派給DexPathList的findClass去查找,在DexPathList的findClass中會去遍歷我們上面創(chuàng)建的dexElements數(shù)組,然后在每個dex中去查找相應(yīng)的類,找到之后就返回,不再向后查找。

4. 補(bǔ)丁包的合并過程:
Patch拿下來之后,會開啟一個Service,把Patch包和Bug Dex合并,并置于tinker的文件加載路徑中。

5. 補(bǔ)丁包的加載流程:
讀取到有合并后的補(bǔ)丁dex,通過反射得到DexPathList中的dexElements數(shù)組,并將新的dex放在數(shù)組的最前面,完成修復(fù)。

二、 資源文件修復(fù)
1. Resource類對象的構(gòu)建過程:
所有的資源文件是通過Resource類獲取到的,創(chuàng)建一個Resource對象需要三個參數(shù)AssetManager、DisplayMetrics和Configuration,后兩個參數(shù)都是全局單例的,直接通過getResources().getDisplayMetrics(),
getResources().getConfiguration()就可以,而一個AssetManager對象的關(guān)鍵就在于addAssetPath方法以指定resource目錄的路徑,直接newInstance后通過反射拿到addAssetPath,invoke傳參一個resDir目錄即可得到對應(yīng)的AssetManager對象。

2. 補(bǔ)丁加載過程:
先將Patch中的Resource和原有Resource合并,而后保存到本地->
通過當(dāng)前的Activity線程去獲取到所有已加載的apk(loadedApk)->
將所有l(wèi)oadedApk的resDir目錄都替換成新的Resource目錄->
創(chuàng)建新的resDir目錄對應(yīng)的AssetManager對象->
替換原有全部的Resource對象里的AssetManager,即可完成修復(fù)。

三、 動態(tài)鏈接庫修復(fù)
Android里面關(guān)于so的加載的兩種方式:
1.? ? System.loadLibrary
這種方式傳入的是so的名字,會直接從系統(tǒng)的目錄去加載so文件,系統(tǒng)的路徑包括/data/data/${package_name}/lib、/system/lib、/vender/lib等這類路徑。
2.? ? System.load
這種方式傳入的是so的絕對路徑,直接從這個路徑加載so文件。
Tinker的so文件熱更新的原理就是通過方式二,直接加載新的so實現(xiàn)的。
1. 補(bǔ)丁生成:
生成補(bǔ)丁時比較新舊so文件使用BSdiff算法生成補(bǔ)丁包。
2. 補(bǔ)丁合成:
下發(fā)補(bǔ)丁成功后根據(jù)BSpatch算法將補(bǔ)丁包和舊的library合成新的library并保存在tinker下面的目錄下:/data/data/${package_name}/tinker/lib。
3. 補(bǔ)丁加載:
通過System.load加載該目錄下面的so文件。
注意:Tinker中so的熱更新對用戶并不是無感的,需要用戶自發(fā)的去加載自己需要的庫文件。
下面是tinker的wiki里關(guān)于這方面的描述:
Tinker并沒有直接將補(bǔ)丁的lib路徑添加到DexPathList中,理論上這樣可以做到程序完全沒有感知的對Library文件作補(bǔ)丁。這里主要是因為在多abi的情況下,某些機(jī)器獲取的并不準(zhǔn)確。
所以想要加載最新的庫,需要自己使用TinkerInstaller.load*Library去加載庫文件,它會自動嘗試先去Tinker中的庫文件加載,加載不成功會調(diào)用System.loadLibrary調(diào)用系統(tǒng)的庫文件。如TinkerInstaller.loadArmLibrary()或TinkerInstaller.loadArmV7Library()
