標籤:
背景
當app發布之後如果出現了緊急的線上bug,整個公司都會為此忙的焦頭爛額,現公司如果線上出現嚴重的P1級bug,甚至大半夜整個項目組都得來緊急修複上線,而bug的原因可能僅僅是傳錯了參數,或者寫錯一行代碼,而且修複後的app又得重新上架,直到使用者更新後bug才會被修正。那熱修複技術的出現就能很大程度上緩解這種情況,修複後不需要重新上架,使用者也不需要重新下載安裝。
原理
github上的熱修複架構如nuwa,HotFix原理都是依據安卓App熱補丁動態修複技術介紹和Android dex分包方案
這兩篇文章,我這裡也只是對這兩篇文章做一個自己的總結加深理解。
關於nuwa架構的使用看另一篇部落格熱修複架構nuwa的使用
熱修複原理是基於Android的分包方案的,那麼什麼是Android的分包方案呢,Android2.3之前執行dexopt的記憶體只有5M,每個dex的方法數不能超過65535,當app功能複雜,類和方法特別多的時候就會在編譯時間報錯。Android dex分包方案中的描述:
當一個app的功能越來越複雜,代碼量越來越多,也許有一天便會突然遇到下列現象:
產生的apk在2.3以前的機器無法安裝,提示INSTALL_FAILED_DEXOPT
方法數量過多,編譯時間出錯,提示:
Conversion to Dalvik format failed:Unable to execute dex: method ID
not in [0, 0xffff]: 65536
出現這種問題的原因是:
Android2.3及以前版本用來執行dexopt(用於最佳化dex檔案)的記憶體只分配了5M
一個dex檔案最多隻支援65536個方法。
解決辦法是將編譯好的class檔案打成兩個dex的包,運行時注入ClassLoader。如何注入呢,先看一下Android的ClassLoader體系,圖片是分包方案裡扒的。
可以看到實作類別有兩個DexClassLoader和PathClassLoader,其中PathClassLoader是用來Android用來載入Android系統類別和應用的載入器,DexClassLoader用來載入.dex和.jar中的class.dex檔案。看一下BaseDexClassLoader載入類的方法,從pathList雷根據類名找,找不到就class not found。pathList是BaseDexClassLoader中的一個對象,它包含一個dexElements集合,找類就是聽過遍曆這個集合,拿到dexFile去找類。
一個ClassLoader可以包含多個dex檔案,每個dex檔案是一個Element,多個dex檔案排列成一個有序的數組dexElements,當找類的時候,會按順序遍曆dex檔案,然後從當前遍曆的dex檔案中找類,如果找類則返回,如果找不到從下一個dex檔案繼續尋找。(來自:安卓App熱補丁動態修複技術介紹)
#BaseDexClassLoader@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException { Class clazz = pathList.findClass(name); if (clazz == null) { throw new ClassNotFoundException(name); } return clazz;}#DexPathListpublic 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;}#DexFilepublic Class loadClassBinaryName(String name, ClassLoader loader) { return defineClass(name, loader, mCookie);}private native static Class defineClass(String name, ClassLoader loader, int cookie);
這樣我們將有bug的類打成patch.jar然後插入到dexElements的最前邊位置,findclass的時候就會從我們的patch.jar 中開始找,找到類之後就返回,有問題的類就被補丁包中的替換掉了。之後還有一個CLASS_ISPREVERIFIED的問題,詳細可以看安卓App熱補丁動態修複技術介紹主要是說,如果類和其引用類如果不在同一個dex包裡就會報錯,校檢出這個錯的前提是引用類被打上了CLASS_ISPREVERIFIED標識,這個標識是什麼時候被打上的呢,在虛擬機器啟動的時候如果一個類的private,static,或者構造方法直接引用到的類都在同一個dex包裡就會給當前類打上這個標識,所以要阻止這個表示被打上,我們要讓類引用一個外部dex包裡的類,往所有的類的建構函式中插入一段
if (ClassVerifier.PREVENT_VERIFY) { System.out.println(AntilazyLoad.class);}public class AntilazyLoad{}
所以我們還需要一個hack.dex,寫個空的類AntilazyLoad打成dex包,而且必須先載入這個包,否則引用這個類的地方就會class not fount,載入方法就和之前的一樣啦,要注意
Application作為應用的入口不能插入這段代碼。(因為載入hack.dex的代碼是在Application中onCreate中執行的,如果在Application的建構函式裡面插入了這段代碼,那麼就是在hack.dex載入之前就使用該類,該類一次找不到,會被永遠的打上找不到的標誌)(來自:安卓App熱補丁動態修複技術介紹)。
android熱修複原理總結