Android熱修復技術選型——三大流派解析

 

2015年以來,Android開發領域裏對熱修復技術的討論和分享愈來愈多,同時也出現了一些不一樣的解決方案,如QQ空間補丁方案、阿里AndFix以 及微信Tinker,它們在原理各有不一樣,適用場景各異,到底採用哪一種方案,是開發者比較頭疼的問題。本文但願經過介紹QQ空間補丁、Tinker以及基於AndFix的阿里百川HotFix技術的原理分析和橫向比較,幫助開發者更深刻了解熱修復方案。數組

 

技術背景微信

——————————————————————————————————————————————————————————————————————函數

1、正常開發流程性能

從流程來看,傳統的開發流程存在不少弊端:測試

·             從新發布版本代價太大優化

·             用戶下載安裝成本過高google

·             BUG修復不及時,用戶體驗太差spa

 

2、熱修復開發流程插件

而熱修復的開發流程顯得更加靈活,優點不少:線程

 

·             無需從新發版,實時高效熱修復

·             用戶無感知修復,無需下載新的應用,代價小

·             修復成功率高,把損失降到最低

 

業界熱門的熱修復技術

——————————————————————————————————————————————————————————————————————————————

熱修復做爲當下熱門的技術,在業界內比較著名的有阿里巴巴的AndFix、Dexposed,騰訊QQ空間的超級補丁和微信的Tinker。最近阿里百川推出的HotFix熱修復服務就基於AndFix技術,定位於線上緊急BUG的即時修復,因此AndFix技術這塊咱們重點分析阿里百川HotFix。下面,咱們就分別介紹QQ空間超級熱補丁技術和微信Tinker以及阿里百川的HotFix技術。

 

1、QQ空間超級補丁技術

超級補丁技術基於DEX分包方案,使用了多DEX加載的原理,大體的過程就是:把BUG方法修復之後,放到一個單獨的DEX裏,插入到dexElements數組的最前面,讓虛擬機去加載修復完後的方法。

當patch.dex中包含Test.class時就會優先加載,在後續的DEX中遇到Test.class的話就會直接返回而不去加載,這樣就達到了修復的目的。

 

可是有一個問題是,當兩個調用關係的類不在同一個DEX時,就會產生異常報錯。咱們知道,在APK安裝時,虛擬機須要將classes.dex優化成odex文件,而後纔會執行。在這個過程當中,會進行類的verify操做,若是調用關係的類都在同一個DEX中的話就會被打上`CLASS_ISPREVERIFIED`的標誌,而後纔會寫入odex文件。

 

因此,爲了能夠正常地進行打補丁修復,必須避免類被打上`CLASS_ISPREVERIFIED`標誌,具體的作法就是單獨放一個類在另外DEX中,讓其餘類調用。

 

咱們來逆向手機QQ空間APK看一下具體的實現:

 

先進入程序入口`QZoneRealApplication`,在`attachBaseContext`中進行了兩步操做:修復`CLASS_ISPREVERIFIED`標誌致使的unexpected DEX problem異常、加載修復的DEX。

 

 1. 修復Unexpected DEX Problem異常

先看代碼,

能夠看到,這裏是要加載一個libs目錄下的dalvikhack.jar。在項目的assets/libs找到該文件,解壓獲得’classes.dex’文件,逆向打開該DEX文件,

 

經過不一樣的DEX加載進來,而後在每個類的構造方法中引用其餘DEX中的惟一類AnitLazyLoad,避免類被打上CLASS_ISPREVERIFIED標誌。

在無修復的狀況下,將DO_VERIFY_CLASSES設置爲false,以提升性能。只有在須要修復的時候,才設置爲true。

 

至於如何加載進來,與下面第二個步驟基本相同。

 

2. 加載修復的DEX

從loadPatchDex()方法進入,通過幾回跳轉,到達核心的代碼段,`SystemClassLoaderInjector.c()`。因爲進行了混淆和屢次方法的跳轉,因而將核心代碼段作了以下整理:

修復的步驟爲:

1. 能夠看出是經過獲取到當前應用的Classloader,即爲BaseDexClassloader

2. 經過反射獲取到他的DexPathList屬性對象pathList

3. 經過反射調用pathList的dexElements方法把patch.dex轉化爲Element[]

4. 兩個Element[]進行合併,把patch.dex放到最前面去

5. 加載Element[],達到修復目的

 

總體的流程圖以下:

從流程圖來看,能夠很明顯的找到這種方式的特色:

優點:

1.      沒有合成整包(和微信Tinker比起來),產物比較小,比較靈活

2.      能夠實現類替換,兼容性高。(某些三星手機不起做用)

不足:

1. 不支持即時生效,必須經過重啓才能生效。

2. 爲了實現修復這個過程,必須在應用中加入兩個dex!dalvikhack.dex中只有一個類,對性能影響不大,可是對於patch.dex來講,修復的類到了必定數量,就須要花很多的時間加載。對手淘這種航母級應用來講,啓動耗時增長2s以上是不可以接受的事。

3. 在ART模式下,若是類修改告終構,就會出現內存錯亂的問題。爲了解決這個問題,就必須把全部相關的調用類、父類子類等等所有加載到patch.dex中,致使補丁包異常的大,進一步增長應用啓動加載的時候,耗時更加嚴重。

 

2、微信Tinker

微信針對QQ空間超級補丁技術的不足提出了一個提供DEX差量包,總體替換DEX的方案。主要的原理是與QQ空間超級補丁技術基本相同,區別在於再也不將patch.dex增長到elements數組中,而是差量的方式給出patch.dex,而後將patch.dex與應用的classes.dex合併,而後總體替換掉舊的DEX文件,以達到修復的目的。

咱們來逆向微信的APK看一下具體的實現:

先找到應用入口`TinkerApplication`,在`onBaseContextAttached()`調用了`loadTinker()`,

進入TinkerLoader的tryLoad()方法中,

從方法名能夠預見,在tryLoadPatchFilesInternal()中嘗試加載本地的補丁,再通過跳轉進入核心修復功能類SystemClassLoaderAdder.class中。

代碼中能夠看出,根據Android版本的不一樣,分別採起具體的修復操做,不過原理都是同樣的。咱們以V19爲例,

從代碼中能夠看到,經過反射操做獲得PathClassLoader的DexPatchList,反射調用patchlist的makeDexElements()方法吧本地的dex文件直接替換到Element[]數組中去,達到修復的目的。

 

對於如何進行patch.dex與classes.dex的合併操做,這裏微信開啓了一個新的進程,開啓新進程的服務TinkerPatchService進行合併。

總體的流程以下:

從流程圖來看,一樣能夠很明顯的找到這種方式的特色:

優點:

1.      合成整包,不用在構造函數插入代碼,防止verify,verify和opt在編譯期間就已經完成,不會在運行期間進行。

2.      性能提升。兼容性和穩定性比較高。

3.      開發者透明,不須要對包進行額外處理。

不足:

1. 與超級補丁技術同樣,不支持即時生效,必須經過重啓應用的方式才能生效。

2. 須要給應用開啓新的進程才能進行合併,而且很容易由於內存消耗等緣由合併失敗。

3. 合併時佔用額外磁盤空間,對於多DEX的應用來講,若是修改了多個DEX文件,就須要下發多個patch.dex與對應的classes.dex進行合併操做時這種狀況會更嚴重,所以合併過程的失敗率也會更高。

 

3、阿里百川HotFix

阿里百川推出的熱修復HotFix服務,相對於QQ空間超級補丁技術和微信Tinker來講,定位於緊急BUG修復的場景下,可以最及時的修復BUG,下拉補丁當即生效無需等待。

一、AndFix實現原理

AndFix不一樣於QQ空間超級補丁技術和微信Tinker經過增長或替換整個DEX的方案,提供了一種運行時在Native修改Filed指針的方式,實現方法的替換,達到即時生效無需重啓,對應用無性能消耗的目的。

原理圖以下:

二、AndFix實現過程

 

對於實現方法的替換,須要在Native層操做,通過三個步驟:

接下來以Dalvik設備爲例,來分析具體的實現過程:

 

一、setup()

 

對於Dalvik來講,遵循JIT即時編譯機制,須要在運行時裝載libdvm.so動態庫,獲取如下內部函數:

1) dvmThreadSelf( ):查詢當前的線程;

2)dvmDecodeIndirectRef( ):根據當前線程得到ClassObject對象。

 

二、setFieldFlag

該操做的目的:把 private、protected的方法和字段都改成public,這樣纔可被動態庫看見並識別,由於動態庫會忽略非public屬性的字段和方法。

 

三、replaceMethod

 

該步驟是方法替換的核心,替換的流程以下:

 

AndFix對ART設備一樣支持,具體的過程與Dalvik類似,這裏再也不贅述。

 

從技術原理,不難看出阿里百川HotFix的幾個特色:

 

優點:

1.      BUG修復的即時性

2.      補丁包一樣採用差量技術,生成的PATCH體積小

3.      對應用無侵入,幾乎無性能損耗

不足:

1.          不支持新增字段,以及修改<init>方法,也不支持對資源的替換。

2.          因爲廠商的自定義ROM,對少數機型暫不支持。

 

綜合分析以下:

 

 

 

熱修復技術的坑與解

——————————————————————————————————————————————————————————————————————

咱們能夠看到,QQ空間超級補丁技術和微信Tinker的修復原理都基於類加載,在功能上已經支持類、資源的替換和新增,功能很是強大。既然已經有了這麼強大的熱修復技術,爲何阿里百川還要推出本身的熱修復方案HotFix呢?

 

1、多DEX帶來的性能影響

咱們知道,多DEX方案原來是用於解決應用方法數65k的問題,如今google也官方支持了MultiDex的實現方案。超級補丁技術和Tinker卻做爲一種熱修復的方案,生平給應用增長了多個DEX,而多DEX技術最大的問題在於性能上的坑,所以基於這種方案的補丁技術影響應用的性能是無疑的。

1. 啓動加載時間過長

咱們能夠看到,超級補丁技術和Tinker都選擇在Application的attachBaseContext()進行補丁dex的加載,即時這是加載dex的最佳時機,可是依然會帶來很大的性能問題,首當其衝的就是啓動時間太長。

對於補丁DEX來講,應用啓動時虛擬機會進行dexopt操做,將patch.dex文件轉換成odex文件,這個過程自己很是耗時。而這個過程又要求在主線程中,以同步的方式執行,不然沒法成功進行修復。就DEX的加載時間,大概作了如下的時間測試。

經過上表能夠看到,隨着patch.dex的尺寸增長,在不作任何優化的狀況下,啓動時間也直線增加。對於一個應用來講,這簡直是災難性的。

 

2. 易形成應用的ANR和Crash

因爲多DEX加載致使了啓動時間變長,這樣更容易引起應用的ANR。咱們知道當應用在主線程等待超過5s之後,就會直接致使長時間無響應而退出。超級補丁技術爲保證ART不出現地址錯亂問題,須要將全部關聯的類所有加入到補丁中,而微信Tinker採起一種差量包合併加載的方式,都會使要加載的DEX體積變得很大。這也很大程度上容易致使ANR狀況的出現。

 

除了應用ANR之外,多DEX模式也一樣很容易致使Crash狀況的出現。在ART設備中爲了保證不出現地址錯亂,須要把修改類的全部相關類所有加入到補丁中,這裏會出現一個問題,爲了保證補丁包的體積最小,可否保證引入所有的關聯類而不引入無關的類呢?一旦沒有引入關聯的類,就會出現如下的異常:

·             NoClassDefFoundError

·             Could Not Find Class

·             Could Not Find Method

出現這些異常,就會直接致使應用的Crash退出。

 

因此,不難看出若是咱們須要修復一個不是Crash的BUG,可是由於未加入相關類而致使了更嚴重的Crash,就更加的得不償失。

 

總的來講,熱修復本質的目的是爲了保證應用更加穩定,而不是爲了更強大的功能引入更大的風險和不穩定性。

 

2、 熱修復 or 插件化?

 

咱們常常提到熱修復和插件化,這都是當下熱門的新興技術。在講述以前,須要對這兩個概念進行一下解釋。

 

·             熱修復:當線上應用出現緊急BUG,爲了不從新發版,而且保證修復的及時性而進行的一項在線推送補丁的修復方案。

·             插件化:一個程序劃分爲不一樣的部分,以插件的形式加載到應用中去,本質上它使用的技術仍是熱修復技術,只是加入了更多工程實踐,讓它支持大規模的代碼更新以及資源和SO包的更新。

 

顯然,從概念上咱們能夠看到,插件化使用場景更可能是功能上的,熱修復強調微小的修復。從這個層面來講,插件化必然功能更增強大,能作的事情也更多。QQ空間超級補丁技術和微信Tinker從類、資源的替換和更新上來看,與其說是熱修復,不如說是插件化技術的實踐。

QQ空間超級補丁技術和微信Tinker提供了更增強大的功能,可是對應用的性能和穩定有較大的影響,就BUG修復的這個使用場景上還不夠明確,而且顯得太重。

 

針對應用的性能損耗,咱們能夠舉例作一個對比:

某APP的啓動載入時間爲3s左右,自己就是基於多DEX模式的實現。

分別接入三種熱修復服務,根據騰訊提供超級補丁技術和Tinker的數據,那麼會變成如下的場景:

1. 阿里百川HotFix:啓動時間幾乎無增長,不增長運行期額外的磁盤消耗。

2. QQ空間超級補丁技術:若是應用有700個類,啓動耗時增長超過2.5s,達到5.5s以上。

3. 微信Tinker:假設應用有5個DEX文件,分別修改了這5個DEX,產生5個patch.dex文件,就要進行5次的patch合併動做,假設每一個補丁1M,那麼就要多佔用7.5M的磁盤空間。

 

顯然對於修復緊急BUG這個場景,阿里百川HotFix的更爲合適,它更加輕量,能夠在不重啓的狀況下生效,且對性能幾乎沒有影響。

 

因此阿里百川針對修復緊急BUG的場景,推出了HotFix這項在線熱修復的輕服務。盡心提供最快捷的修復,讓用戶作到真正無感知,即時生效。 相比較微信Tinker、QQ空間超級補丁技術更多地把場景定位在發佈新功能上,採用ClassLoader的模式,以犧牲少許性能爲代價去實現類、資源新增或替換的功能,阿里百川HotFix對應用自己作到無侵入,幾乎無性能損耗。

 

總結

QQ空間超級補丁技術和微信Tinker 支持新增類和資源的替換,在一些功能化的更新上更爲強大,但對應用的性能和穩定會有的必定的影響;阿里百川HotFix雖然暫時不支持新增類和資源的替換,對新功能的發佈也有所限制,可是做爲一項定位爲線上緊急BUG的熱修復的服務來講,可以真正作到BUG即時修復用戶無感知,同時保證對應用性能不產生沒必要要的損耗,在熱修復方面不失爲一個好的選擇。

目前阿里百川HotFix已經開始公測,點擊當即使用,便可開始你的熱修復之旅。

相關文章
相關標籤/搜索