Android熱更新六:Qzone熱更新原理

很早之前就想深入的研究和學(xué)習(xí)一下熱修復(fù),由于時(shí)間的原因一直拖著,現(xiàn)在才執(zhí)筆弄起來(lái)。


Android而更新系列:
Android熱更新一:JAVA的類(lèi)加載機(jī)制
Android熱更新二:理解Java反射
Android熱更新三:Android類(lèi)加載機(jī)制
Android熱更新四:熱修復(fù)機(jī)制
Android熱更新五:四大熱修復(fù)方案分析
Android熱更新六:Qzone熱更新原理
Android熱更新七:Tinker熱更新原理
Android熱更新八:AndFix熱更新原理
Android熱更新九:Robust熱更新原理
Android熱更新十:自己寫(xiě)一個(gè)Android熱修復(fù)


??超級(jí)補(bǔ)丁技術(shù)基于DEX分包方案,使用了多DEX加載的原理,大致的過(guò)程就是:把BUG方法修復(fù)以后,放到一個(gè)單獨(dú)的DEX里,插入到dexElements數(shù)組的最前面,讓虛擬機(jī)去加載修復(fù)完后的方法。


??當(dāng)patch.dex中包含Test.class時(shí)就會(huì)優(yōu)先加載,在后續(xù)的DEX中遇到Test.class的話(huà)就會(huì)直接返回而不去加載,這樣就達(dá)到了修復(fù)的目的。

??但是有一個(gè)問(wèn)題是,當(dāng)兩個(gè)調(diào)用關(guān)系的類(lèi)不在同一個(gè)DEX時(shí),就會(huì)產(chǎn)生異常報(bào)錯(cuò)。我們知道,在APK安裝時(shí),虛擬機(jī)需要將classes.dex優(yōu)化成odex文件,然后才會(huì)執(zhí)行。在這個(gè)過(guò)程中,會(huì)進(jìn)行類(lèi)的verify操作,如果調(diào)用關(guān)系的類(lèi)都在同一個(gè)DEX中的話(huà)就會(huì)被打上CLASS_ISPREVERIFIED的標(biāo)志,然后才會(huì)寫(xiě)入odex文件。

??所以,為了可以正常地進(jìn)行打補(bǔ)丁修復(fù),必須避免類(lèi)被打上CLASS_ISPREVERIFIED標(biāo)志,具體的做法就是單獨(dú)放一個(gè)類(lèi)在另外DEX中,讓其他類(lèi)調(diào)用。

??我們來(lái)逆向手機(jī)QQ空間APK看一下具體的實(shí)現(xiàn):

??先進(jìn)入程序入口QZoneRealApplication,在attachBaseContext中進(jìn)行了兩步操作:修復(fù)CLASS_ISPREVERIFIED標(biāo)志導(dǎo)致的unexpected DEX problem異常、加載修復(fù)的DEX。

1. 修復(fù)Unexpected DEX Problem異常

??先看代碼,


??可以看到,這里是要加載一個(gè)libs目錄下的dalvikhack.jar。在項(xiàng)目的assets/libs找到該文件,解壓得到’classes.dex’文件,逆向打開(kāi)該DEX文件,


??通過(guò)不同的DEX加載進(jìn)來(lái),然后在每一個(gè)類(lèi)的構(gòu)造方法中引用其他DEX中的唯一類(lèi)AnitLazyLoad,避免類(lèi)被打上CLASS_ISPREVERIFIED標(biāo)志。


??在無(wú)修復(fù)的情況下,將DO_VERIFY_CLASSES設(shè)置為false,以提高性能。只有在需要修復(fù)的時(shí)候,才設(shè)置為true。


??至于如何加載進(jìn)來(lái),與下面第二個(gè)步驟基本相同。

2. 加載修復(fù)的DEX

??從loadPatchDex()方法進(jìn)入,經(jīng)過(guò)幾次跳轉(zhuǎn),到達(dá)核心的代碼段,SystemClassLoaderInjector.c()。由于進(jìn)行了混淆和多次方法的跳轉(zhuǎn),于是將核心代碼段做了如下整理:

修復(fù)的步驟為:

  1. 可以看出是通過(guò)獲取到當(dāng)前應(yīng)用的Classloader,即為BaseDexClassloader
  2. 通過(guò)反射獲取到他的DexPathList屬性對(duì)象pathList
  3. 通過(guò)反射調(diào)用pathList的dexElements方法把patch.dex轉(zhuǎn)化為Element[]
  4. 兩個(gè)Element[]進(jìn)行合并,把patch.dex放到最前面去
  5. 加載Element[],達(dá)到修復(fù)目的

整體的流程圖如下:


??從流程圖來(lái)看,可以很明顯的找到這種方式的特點(diǎn):

優(yōu)勢(shì):

沒(méi)有合成整包(和微信Tinker比起來(lái)),產(chǎn)物比較小,比較靈活
可以實(shí)現(xiàn)類(lèi)替換,兼容性高。(某些三星手機(jī)不起作用)

不足:
  1. 不支持即時(shí)生效,必須通過(guò)重啟才能生效。
  2. 為了實(shí)現(xiàn)修復(fù)這個(gè)過(guò)程,必須在應(yīng)用中加入兩個(gè)dex!dalvikhack.dex中只有一個(gè)類(lèi),對(duì)性能影響不大,但是對(duì)于patch.dex來(lái)說(shuō),修復(fù)的類(lèi)到了一定數(shù)量,就需要花不少的時(shí)間加載。對(duì)手淘這種航母級(jí)應(yīng)用來(lái)說(shuō),啟動(dòng)耗時(shí)增加2s以上是不能夠接受的事。
  3. 在ART模式下,如果類(lèi)修改了結(jié)構(gòu),就會(huì)出現(xiàn)內(nèi)存錯(cuò)亂的問(wèn)題。為了解決這個(gè)問(wèn)題,就必須把所有相關(guān)的調(diào)用類(lèi)、父類(lèi)子類(lèi)等等全部加載到patch.dex中,導(dǎo)致補(bǔ)丁包異常的大,進(jìn)一步增加應(yīng)用啟動(dòng)加載的時(shí)候,耗時(shí)更加嚴(yán)重。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容