Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android熱修復技術選型——三大流派解析

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

編輯:關於Android編程

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

技術背景

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

一、正常開發流程

\

從流程來看,傳統的開發流程存在很多弊端:

·重新發布版本代價太大

·用戶下載安裝成本太高

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

二、熱修復開發流程

\

而熱修復的開發流程顯得更加靈活,優勢很多:

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

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

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

業界熱門的熱修復技術

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

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

一、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. 修復UnexpectedDEX 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中,導致補丁包異常的大,進一步增加應用啟動加載的時候,耗時更加嚴重。

二、微信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進行合並操作時這種情況會更嚴重,因此合並過程的失敗率也會更高。

三、阿裡百川HotFix

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

\

1、AndFix實現原理

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

原理圖如下:

\

2、AndFix實現過程

對於實現方法的替換,需要在Native層操作,經過三個步驟:

\

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

1、setup()

\

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

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

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

2、setFieldFlag

\

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

3、replaceMethod

\

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

AndFix對ART設備同樣支持,具體的過程與Dalvik相似,這裡不再贅述。

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

優勢:

1.BUG修復的即時性

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

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

不足:

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

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

綜合分析如下:

\

熱修復技術的坑與解

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

我們可以看到,QQ空間超級補丁技術和微信Tinker的修復原理都基於類加載,在功能上已經支持類、資源的替換和新增,功能非常強大。既然已經有了這麼強大的熱修復技術,為什麼阿裡百川還要推出自己的熱修復方案HotFix呢?

一、多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,就更加的得不償失。

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

二、熱修復 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對應用本身做到無侵入,幾乎無性能損耗。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved