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