熱修復(fù)作為當(dāng)下熱門的技術(shù),在業(yè)界內(nèi)比較著名的有阿里巴巴的AndFix、Dexposed,騰訊QQ空間的超級補(bǔ)釘技術(shù)和微信的Tinker。最近阿里百川推出的HotFix熱修復(fù)服務(wù)就基于AndFix技術(shù),定位于線上緊急BUG的即時(shí)修復(fù),所以AndFix技術(shù)這塊我們重點(diǎn)分析阿里百川HotFix。下面,我們就分別介紹QQ空間超級熱補(bǔ)釘技術(shù)和微信的Tinker和阿里百川HotFix技術(shù)
超級補(bǔ)釘技術(shù)基于DEX分包方案,使用了多DEX加載的原理,大致的進(jìn)程就是:把BUG方法修復(fù)以后,放到1個(gè)單獨(dú)的DEX里,插入到dexElements數(shù)組的最前面,讓虛擬機(jī)去加載修復(fù)完后的方法。
當(dāng)patch.dex中包括Test.class時(shí)就會(huì)優(yōu)先加載,在后續(xù)的DEX中遇到Test.class的話就會(huì)直接返回而不去加載,這樣就到達(dá)了修復(fù)的目的。
但是有1個(gè)問題是,當(dāng)兩個(gè)調(diào)用關(guān)系的類不在同1個(gè)DEX時(shí),就會(huì)產(chǎn)生異常報(bào)錯(cuò)。我們知道,在APK安裝時(shí),虛擬機(jī)需要將classes.dex優(yōu)化成odex文件,然后才會(huì)履行。在這個(gè)進(jìn)程中,會(huì)進(jìn)行類的verify操作,如果調(diào)用關(guān)系的類都在同1個(gè)DEX中的話就會(huì)被打上CLASS_ISPREVERIFIED的標(biāo)志,然后才會(huì)寫入odex文件。
所以,為了可以正常的進(jìn)行打補(bǔ)釘修復(fù),必須避免類被打上CLASS_ISPREVERIFIED標(biāo)志,具體的做法就是單獨(dú)放1個(gè)類在另外DEX中,讓其他類調(diào)用。
我們來逆向手機(jī)QQ空間APK看1下具體的實(shí)現(xiàn):
先進(jìn)入程序入口QZoneRealApplication,在attachBaseContext中進(jìn)行了兩步操作:修復(fù)CLASS_ISPREVERIFIED標(biāo)志致使的unexpected DEX problem異常、加載修復(fù)的DEX。
優(yōu)勢
1. 沒有合成整包(和微信Tinker比起來),產(chǎn)物比較小,比較靈活
2. 可以實(shí)現(xiàn)類替換,兼容性高。(某些3星手機(jī)不起作用)
不足
1. 不支持即時(shí)生效,必須通太重啟才能生效
2. 實(shí)現(xiàn)修復(fù)這個(gè)進(jìn)程,必須在利用中加入兩個(gè)dex!dalvikhack.dex中只有1個(gè)類,對性能影響不大,但是對patch.dex來講,修復(fù)的類到了1定數(shù)量,就需要花很多的時(shí)間加載。對手淘這類航母級利用來講,啟動(dòng)耗時(shí)增加2s以上是不能夠接受的事
3. 在ART模式下,如果類修改了結(jié)構(gòu),就會(huì)出現(xiàn)內(nèi)存錯(cuò)亂的問題。為了解決這個(gè)問題,就必須把所有相干的調(diào)用類、父類子類等等全部加載到patch.dex中,致使補(bǔ)釘包異常的大,進(jìn)1步增加利用啟動(dòng)加載的時(shí)候,耗時(shí)更加嚴(yán)重
微信針對QQ空間超級補(bǔ)釘技術(shù)的不足提出了1個(gè)提供DEX差量包,整體替換DEX的方案。主要的原理是與QQ空間超級補(bǔ)釘技術(shù)基本相同,區(qū)分在于不再將patch.dex增加到elements數(shù)組中,而是差量的方式給出patch.dex,然后將patch.dex與利用的classes.dex合并,然后整體替換掉舊的DEX,到達(dá)修復(fù)的目的
* 進(jìn)入TinkerLoader的tryLoad()方法中
* 從方法名可以預(yù)感,在tryLoadPatchFilesInternal()中嘗試加載本地的補(bǔ)釘,再經(jīng)過跳轉(zhuǎn)進(jìn)入核心修復(fù)功能類SystemClassLoaderAdder.class中
* 代碼中可以看出,根據(jù)Android版本的不同,分別采取具體的修復(fù)操作,不過原理都是1樣的。我們以V19為例
* 從代碼中可以看到,通過反射操作得到PathClassLoader的DexPatchList,反射調(diào)用patchlist的makeDexElements()方法吧本地的dex文件直代替換到Element[]數(shù)組中去,到達(dá)修復(fù)的目的。
對如何進(jìn)行patch.dex與classes.dex的合并操作,這里微信開啟了1個(gè)新的進(jìn)程,開啟新進(jìn)程的服務(wù)TinkerPatchService進(jìn)行合并
優(yōu)勢
1. 合成整包,不用在構(gòu)造函數(shù)插入代碼,避免verify,verify和opt在編譯期間就已完成,不會(huì)在運(yùn)行期間進(jìn)行
2. 性能提高。兼容性和穩(wěn)定性比較高。
3. 開發(fā)者透明,不需要對包進(jìn)行額外處理
不足
1. 與超級補(bǔ)釘技術(shù)1樣,不支持即時(shí)生效,必須通太重啟利用的方式才能生效。
2. 需要給利用開啟新的進(jìn)程才能進(jìn)行合并,并且很容易由于內(nèi)存消耗等緣由合并失敗。
3. 占用額外磁盤空間,對多DEX的利用來講,如果修改了多個(gè)DEX文件,就需要下發(fā)多個(gè)patch.dex與對應(yīng)的classes.dex進(jìn)行合并操作時(shí)這類情況會(huì)更嚴(yán)重,因此合并進(jìn)程的失敗率也會(huì)更高。
阿里百川推出的熱修復(fù)HotFix服務(wù),相對QQ空間超級補(bǔ)釘技術(shù)和微信Tinker來講,定位于緊急bug修復(fù)的場景下,能夠最及時(shí)的修復(fù)bug,下拉補(bǔ)釘立即生效無需等待
該步驟是方法替換的核心,替換的流程以下
優(yōu)勢
1. BUG修復(fù)的即時(shí)性
2. 補(bǔ)釘包一樣采取差量技術(shù),生成的PATCH體積小
3. 對利用無侵入,幾近無性能消耗
不足
1. 不支持新增字段,和修改方法,也不支持對資源的替換。
2. 由于廠商的自定義ROM,對少數(shù)機(jī)型暫不支持
我們可以看到,QQ空間超級補(bǔ)釘技術(shù)和微信Tinker的修復(fù)原理都基于類加載,在功能上已支持類、資源的替換和新增,功能非常強(qiáng)大。既然已有了這么強(qiáng)大的熱修復(fù)技術(shù),為何阿里百川還要推出自己的熱修復(fù)方案HotFix呢?
我們知道,多DEX方案用來解決利用方法數(shù)65k的問題,現(xiàn)在Google也官方支持了MultiDex的實(shí)現(xiàn)方案。但是,這實(shí)在是利用因方法數(shù)超越而作出的不得已的下策,但是超級補(bǔ)釘技術(shù)和Tinker作為1種熱修復(fù)的方案,平生給利用增加了多個(gè)DEX,而多DEX技術(shù)最大的問題在于性能上的坑,因此基于這類方案的補(bǔ)釘技術(shù)影響利用的性能是無疑的
我們可以看到,超級補(bǔ)釘技術(shù)和Tinker都選擇在Application的attachBaseContext()進(jìn)行補(bǔ)釘dex的加載,即便這是加載dex的最好時(shí)機(jī),但是仍然會(huì)帶來很大的性能問題,首當(dāng)其沖的就是啟動(dòng)時(shí)間太長。
對補(bǔ)釘DEX來講,利用啟動(dòng)時(shí)虛擬機(jī)會(huì)進(jìn)行dexopt操作,將patch.dex文件轉(zhuǎn)換成odex文件,這個(gè)進(jìn)程非常耗時(shí)。而這個(gè)進(jìn)程,又要求需要在主線程中,以同步的方式履行,否則沒法成功進(jìn)行修復(fù)。就DEX的加載時(shí)間,大概做了以下的時(shí)間測試
![]()
隨著patch.dex的增加,在不做任何優(yōu)化的情況下,啟動(dòng)時(shí)間也直線增長。對1個(gè)利用來講,這簡直是災(zāi)害性的
正是特別多DEX加載致使了啟動(dòng)時(shí)間太長,很容易就會(huì)引發(fā)利用的ANR。我們知道當(dāng)利用在主線程等待超過5s以后,就會(huì)直接致使長時(shí)間無響應(yīng)而退出。超級補(bǔ)釘技術(shù)為保證ART不出現(xiàn)地址錯(cuò)亂問題,需要將所有關(guān)聯(lián)的類全部加入到補(bǔ)釘中,而微信Tinker采取1種差量包合并加載的方式,都會(huì)使要加載的dex體積變得很大。這也很大程度上容易致使ANR情況的出現(xiàn)。
除利用ANR之外,多DEX模式也一樣很容易致使Crash情況的出現(xiàn)。我們知道,超級補(bǔ)釘技術(shù)為了保證ART裝備下不出現(xiàn)地址錯(cuò)亂問題,需要把修改類的所有相干類全部加入到補(bǔ)釘中,這里會(huì)出現(xiàn)1個(gè)問題,為了保證補(bǔ)釘包的體積最小,能否保證引入全部的關(guān)聯(lián)類而不引入無關(guān)的類呢?1旦沒有引入關(guān)聯(lián)的類,就會(huì)出現(xiàn)以下的異常:
1. NoClassDefFoundError
2. Could not find class
3. Could not find method
出現(xiàn)這些異常,就會(huì)直接致使利用的Crash退出。
所以,不難看出如果我們需要修復(fù)1個(gè)不是Crash的BUG,但是由于未加入相干類而致使了更嚴(yán)重的Crash,就更加的得不償失。
總的來講,熱修復(fù)本質(zhì)的目的是為了保證利用更加穩(wěn)定,而不是為了更強(qiáng)大的功能引入更大的風(fēng)險(xiǎn)和不穩(wěn)定性。
插件化:1個(gè)程序劃分為不同的部份,以插件的情勢加載到利用中去,本質(zhì)上它使用的技術(shù)還是熱修復(fù)技術(shù),只是加入了更多工程實(shí)踐,讓它支持大范圍的代碼更新和資源和SO包的更新。
熱修復(fù):當(dāng)線上利用出現(xiàn)緊急BUG,為了不重新發(fā)版,并且保證修復(fù)的及時(shí)性而進(jìn)行的1項(xiàng)在線推送補(bǔ)釘?shù)男迯?fù)方案。
從概念上我們可以看到,插件化使用處景更多是功能,熱修復(fù)使用常見在于修復(fù)。從這個(gè)層面來講,插件化必定功能更加強(qiáng)大,能做的事情也更多。QQ空間超級補(bǔ)釘技術(shù)和微信Tinker從類、資源的替換和更新上來看,與其說是熱修復(fù),不如說是插件化。
固然,強(qiáng)大的功能也就增加了不穩(wěn)定的因素。比如上文提到的增加啟動(dòng)時(shí)間,致使ANR、Crash的問題。
QQ空間超級補(bǔ)釘技術(shù)和微信Tinker提供了更加強(qiáng)大的功能,但是對利用的性能和穩(wěn)定有較大的影響,就BUG修復(fù)的這個(gè)使用處景上還不夠明確,并且顯得太重
針對利用的性能消耗,我們可以舉例做1個(gè)對照
明顯對修復(fù)緊急BUG這個(gè)場景,阿里百川HotFix的更加適合,它更加輕量,可以在不重啟的情況下生效,且對性能幾近沒有影響。微信Tinker、QQ空間超級補(bǔ)釘技術(shù)更多地把場景定位在發(fā)布小的新功能上,采取ClassLoader的模式,犧牲較高的性能代價(jià)去實(shí)現(xiàn)類、資源新增或替換的功能。阿里百川HotFix對利用本身做到無侵入,無性能消耗
QQ空間超級補(bǔ)釘技術(shù)和微信Tinker 支持新增類和資源的替換,在1些功能化的更新上更加強(qiáng)大,但對利用的性能和穩(wěn)定會(huì)有的1定的影響;阿里百川HotFix雖然暫時(shí)不支持新增類和資源的替換,對新功能的發(fā)布也有所限制,但是作為1項(xiàng)定位為線上緊急BUG的熱修復(fù)的服務(wù)來講,能夠真正做到BUG即時(shí)修復(fù)用戶無感知,同時(shí)保證對利用性能不產(chǎn)生沒必要要的消耗,在熱修復(fù)方面不失為1個(gè)好的選擇!