Android應(yīng)用經(jīng)常會(huì)遇到App上線后發(fā)現(xiàn)Bug需要緊急修復(fù),如果將修復(fù)Bug后的應(yīng)用重新提交應(yīng)用商店進(jìn)行審核升級(jí)考余,首先會(huì)遇到應(yīng)用商店審核需要時(shí)間技即,還可能會(huì)出現(xiàn)審核不通過(guò)的情況未蝌,其次還得提示用戶(hù)進(jìn)行升級(jí)蔽午,有的用戶(hù)可能還不愿意升級(jí)易茬,如果應(yīng)用包比較大,下載需要時(shí)間比較長(zhǎng),用戶(hù)升級(jí)的意愿進(jìn)一步降低抽莱。如果能有一種技術(shù)可以只將修復(fù)Bug涉及到的相關(guān)文件打包上傳到服務(wù)器范抓,客戶(hù)端只需要下載少量文件就能完成Bug的修復(fù),就會(huì)避免上面所說(shuō)的幾個(gè)問(wèn)題食铐。應(yīng)市場(chǎng)需求匕垫,Android熱修復(fù)應(yīng)運(yùn)而生。
Android熱修復(fù)方案基本分為兩種:
- 類(lèi)替換方案
- Native修改Filed指針替換方法
本文主要講第一種類(lèi)替換的方式虐呻。
類(lèi)加載機(jī)制
說(shuō)起Android熱修復(fù)就不得不提類(lèi)加載機(jī)制象泵,Android是基于Java進(jìn)行開(kāi)發(fā)的,首先說(shuō)一下Java的類(lèi)加載機(jī)制铃慷。
雙親委托機(jī)制
Java的類(lèi)加載是通過(guò)ClassLoader進(jìn)行完成的单芜,ClassLoaer 的加載機(jī)制是一種特別聰明的方式,雙親委托機(jī)制犁柜,在這種機(jī)制下,一個(gè)Class只會(huì)被加載一次堂淡。這里需要明白的一點(diǎn)是對(duì)于一個(gè)ClassLoader(類(lèi)加載器)來(lái)說(shuō)馋缅,將一個(gè)具體的類(lèi)(class)加載到內(nèi)存中其實(shí)是由虛擬機(jī)完成的。Java中的類(lèi)加載主要分為4種:
- Bootstrap ClassLoader:加載Java虛擬機(jī)運(yùn)行時(shí)所需要的系統(tǒng)類(lèi)绢淀,如java.lang.萤悴、java.uti.等這些系統(tǒng)類(lèi)
- Extensions ClassLoader:用于加載 Java 的拓展類(lèi),主要實(shí)現(xiàn)系統(tǒng)類(lèi)之外的額外功能
- App ClassLoader:負(fù)責(zé)加載當(dāng)前應(yīng)用程序Classpath目錄下的所有jar和Class文件
- Custom ClassLoader: 自定義的類(lèi)加載器
Android的類(lèi)加載器
在Android中類(lèi)的加載也是通過(guò)ClassLoader來(lái)完成皆的,具體來(lái)說(shuō)就是PathClassLoader 和 DexClassLoader覆履。實(shí)現(xiàn)Android熱修復(fù)需要了解以下四個(gè)類(lèi):
- PathClassLoader:只能加載已經(jīng)安裝到Android系統(tǒng)中的apk文件(/data/app目錄),是Android默認(rèn)使用的類(lèi)加載器费薄。
- DexClassLoader:可以加載任意目錄下的dex/jar/apk/zip文件硝全,這里可以是我們熱修復(fù)需要用到的補(bǔ)丁文件。
- BaseDexClassLoader:PathClassLoader和DexClassLoader共同的父類(lèi)楞抡。
- DexPathList:將熱修復(fù)補(bǔ)丁文件封裝成Element對(duì)象伟众,并將這些對(duì)象添加到一個(gè)Element的數(shù)組集合dexElements中去。
其中PathClassLoader召廷、DexClassLoader這兩個(gè)類(lèi)都是繼承自BaseDexClassLoader凳厢,在BaseDexClassLoader的構(gòu)造函數(shù)中對(duì)DexPathList進(jìn)行初始化。
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(parent);
this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
熱修復(fù)簡(jiǎn)單實(shí)現(xiàn)原理
在App出現(xiàn)Bug后竞慢,可以將修復(fù)完Bug的幾個(gè)類(lèi)打包成一個(gè)補(bǔ)丁文件先紫,然后通過(guò)DexClassLoader找到補(bǔ)丁文件路徑將補(bǔ)丁文件加載到內(nèi)存中,在DexPathList初始化完成后筹煮,通過(guò)DexPathList將補(bǔ)丁文件封裝出一個(gè)Element對(duì)象遮精,并且將這個(gè)Element對(duì)象插到原有dexElements數(shù)組的最前端。當(dāng)DexClassLoader去加載類(lèi)時(shí)寺谤,優(yōu)先會(huì)從我們插入的這個(gè)Element中找到相應(yīng)的類(lèi)仑鸥,雖然那個(gè)有bug的類(lèi)還存在于數(shù)組中后面的Element中吮播,但由于雙親加載機(jī)制的特點(diǎn),這個(gè)有bug的類(lèi)已經(jīng)沒(méi)有機(jī)會(huì)被加載了眼俊,這樣一個(gè)bug就在沒(méi)有重新安裝應(yīng)用的情況下修復(fù)了意狠。