前段時間熱修復這個詞非常火,當時只是大體看了一下,今天抽空好好看了一下具體原理.
什麼是熱修復?
簡單的說就是用戶不用重新下載一個新的apk安裝,而是直接下載一個補丁包,通過補丁來替換一些出現bug的類, 當然下載補丁的過程用戶一般是感覺不到的,表面上看是直接修復了bug.
原理
類似與插件開發,關於插件開發原理,看這篇Android插件原理剖析,其中介紹了一下java中的類加載器和android中的類加載器. 熱修復就是利用android中的
DexClassLoader
類加載器,動態加載補丁dex,替換有bug的類
已有的熱修復解決方案:
- https://github.com/dodola/HotFix
- https://github.com/jasonross/Nuwa
- https://github.com/bunnyblue/DroidFix
這幾個庫的原理都類似,具體描述可以查看這篇安卓App熱補丁動態修復技術介紹.
切入點
想修復方法? 方法在哪裏? 方法都包含在類中. 類在哪裏? 類被包含在dex中,而
最根本的來源是下面這段代碼:
1 2 3 4 5 6 7 8 9 10 11 12 |
public Class findClass(String name) { for (Element element : dexElements) { DexFile dex = element.dexFile; if (dex != null) { Class clazz = dex.loadClassBinaryName(name, definingContext); if (clazz != null) { return clazz; } } } return null; } |
可以看出呢,BaseDexClassLoader中有個pathList
對象,pathList中包含一個DexFile的集合dexElements
,而對於類加載呢,就是遍歷這個集合,通過DexFile去尋找,一個ClassLoader可以包含多個dex文件,每個dex文件是一個Element,多個dex文件排列成一個有序的數組dexElements,當找類的時候,會按順序遍歷dex文件,然後從當前遍歷的dex文件中找類,如果找類則返回,如果找不到從下一個dex文件繼續查找。
簡單來說: 首先找到pathList
對象,然後通過反射改變dexElements
數組.
但是遇到的以下問題: 也就是類被打上了 CLASS_ISPREVERIFIED 標誌
1
|
java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
|
根據QQ空間的文章, 這個錯誤是因爲虛擬機加載類的時候, 當一個類中的直接方法(Direct Method)來自與同一個dex中,那麼這個類就會被加上 CLASS_ISPREVERIFIED 標誌, 再次通過類加載器加載會出現上面的錯誤.
解決方案: 在需要加載的類中,引用一個別的dex中的類.這樣這個類就不會被加上 CLASS_ISPREVERIFIED 標誌了,然後就可以再次加載了. 上面一個開源庫的普遍方案就是在類的 默認構造方法 上面添加一個其他dex的引用.
總結
於是總體步驟如下:
- 可能出現bug的類中,引用一下別的dex中的類.
- 因爲1中引用了別的dex,需要先將別的dex加載進來.
- 現在可以替換bug的類了,加載補丁,通過反射將補丁中的dex放到
pathList
對象的dexElements
數組的前面,完成打補丁.
其中兩個注意點:
- 防止類被加上 CLASS_ISPREVERIFIED 標誌(通過修改類默認構造方法)
- 生成補丁dex(通過dx工具)
參考文章: