Andfix熱修復技術淺析

By吳思博

一、什么是熱修復:

二、技術背景

三、當前主流的熱修復方案比較

四、Andfix的介紹與實踐(開源)

1原理

2基本使用

五、其他方案介紹

一、什么是熱修復:

熱修復(HotFix)是以補丁的方式動態修復緊急Bug,不再需要重新發布App,不需要用戶重新下載覆蓋安裝的方式來實現代碼的替換修改。

二、技術背景

(1)正常開發流程

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

1、重新發布版本代價太大

2、用戶下載安裝成本太高

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

(2)熱修復開發流程

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

1、無需重新發版,實時高效修復

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

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

三、當前主流的熱修復方案比較:

熱修復技術近期變得越來越熱門,同時也出現了一些不同的解決方案,如阿里的AndFix(開源)、QQ空間補丁方案、以及微信的Tinker方案,但是它們的原理、適用場景都各有不同。我們項目中可以采用哪種方案,是我們比較關注的問題。

QQ空間超級補丁技術和微信Tinker支持新增類和資源的替換,在一些功能化的更新上更為強大,但對應用的性能和穩定會有的一定的影響;AndFix雖然暫時不支持新增類和資源的替換,對新功能的發布也有所限制,作為定位為線上緊急BUG的熱修復的服務來說,還是比較好的,同時對應用性能不產生不必要的損耗,在熱修復方面不失為一個好的選擇。

四、Andfix的介紹與實踐(定位:一個低成本快速接入的熱修復第一方案)

Github:https://github.com/alibaba/AndFix

(1)AndFix是一個Android App的在線熱補丁框架。使用此框架,我們能夠在不重復發版的情況下,在線修改App中的Bug。AndFix就是“android Hot-Fix”的縮寫。就目前來說,AndFix支持Android 2.3到7.0版本,并且支持arm與X86系統架構的設備。支持Dalvik與ART的Runtime。AndFix的補丁文件是以.apatch結尾的文件。

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

AndFix實現過程:

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

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

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

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

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

setFieldFlag

動態庫會忽略非public屬性的字段和方法,該操作的目的:讓private、protected的方法和字段可被動態庫看見并識別。

replaceMethod:

AndFix對ART設備同樣支持,具體的過程與Dalvik相似。

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

優點:

1、BUG修復的即時

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

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

不足:

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

2、由于廠商的自定義ROM,對少數機型可能暫時不支持。

(2)Andfix的基本使用。

1.在自定義Application中初始化,為了更早的修復應用中的bug。

package com.euler.andfix;

import android.app.Application;

import com.alipay.euler.andfix.patch.PatchManager;

public class MainApplication extends Application {

public PatchManager mPatchManager;

@Override

public void onCreate() {

super.onCreate();

//初始化patch管理類

mPatchManager = new PatchManager(this);

//初始化patch版本

mPatchManager.init("1.0");

//加載已經添加到PatchManager中的patch

mPatchManager.loadPatch();

}

}

2.如果有新的補丁需要修復,下載完成后,進行以下操作

//添加patch,只需指定patch的路徑即可,補丁會立即生效

mPatchManager.addPatch(path);

3.當apk版本升級,需要把之前patch文件的刪除,需要以下操作

//刪除所有已加載的patch文件

mPatchManager.removeAllPatch();

我們也可封裝成一個工具類

patch文件的生成

使用工具:apkpatch-1.0.3

原理:根據兩個apk包,生成一個差異文件,就是所謂的補丁文件即patch文件。

命令: apkpatch.bat -f new.apk -t old.apk -o output1 -k debug.keystore -p android -a androiddebugkey -e android

-f :新版本

-t :舊版本

-o :輸出目錄

-k :打包所用的keystore

-p :keystore的密碼

-a :keystore用戶別名

-e :keystore用戶別名密碼

執行完命令,就會在輸出目錄中輸出.apatch文件:

new-c293df7dbc23f11214fdd020ea78d3b8.apatch

:就是patch文件。

.apatch文件根目錄內容:

META_INF

文件下內容:

PATCH.MF

文件內容:

注:Patch-Classes就是改動過的class.

客戶端請求服務器接口(api),服務器根據用戶傳遞的數據分析是否有需要修復的bug。如果有bug需要修復,就下載服務器指定的.apatch文件的鏈接,下載完后及時加載并修復,使用addpatch(path)方法,補丁會立即生效。

在Android Studio

dependencies {

compile 'com.alipay.euler:andfix:0.3.1@aar'

}

代碼混淆(ProGuard)

-keep class * extends java.lang.annotation.Annotation

-keepclasseswithmembernames class * {

native ;

}

Q & A:

1、如何解決某些機型不兼容的問題?還有采用YunOS系統的?之前測試在ART模式機型上,偶現崩潰,不知采用了什么機制處理的?線上采用Andfix,會不會風險比較高?

ART的機型上暫時未出現崩潰的現象。對于機型的兼容性問題,采取的措施是做平臺區間控制基本兼容,對于個別機型的問題,可以參考統計數據加入黑名單,與服務器約定協議,黑名單上的機型不走andifx流程。美聊線上實踐暫時未出現不可控的問題

2、沒有成功更新的情況下,會不會引起app的崩潰?

更新不成功不會引起崩潰

五、其他技術簡介

android的類加載器分為兩種,PathClassLoader和DexClassLoader,兩者都繼承自BaseDexClassLoader

PathClassLoader用來加載系統類和應用類。DexClassLoader用來加載jar、apk、dex文件.加載jar、apk也是最終抽取里面的Dex文件進行加載。

1、QQ空間超級補丁技術

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

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

但是有一個問題是,當兩個調用關系的類在同一個DEX時,就會產生異常報錯。在APK安裝時,虛擬機需要將classes.dex優化成odex文件,然后才會執行。在這個過程中,會進行類的verify操作,如果方法中直接引用到的類(第一層級關系,不會進行遞歸搜索)和class都在同一個dex中的話,那么這個類就會被打上CLASS_ISPREVERIFIED,然后才會寫入odex文件。所以,為了可以正常的進行打補丁修復,必須避免類被打上CLASS_ISPREVERIFIED標志,具體的做法就是單獨放一個類在另外DEX中,讓其他類調用。

其實就是兩件事:

1、動態改變BaseDexClassLoader對象間接引用的dexElements;

2、在app打包的時候,阻止相關類去打上CLASS_ISPREVERIFIED標志。

手機QQ空間APK具體的實現:先進入程序入口QZoneRealApplication,在attachBaseContext中進行了兩步操作:(1)修復CLASS_ISPREVERIFIED標志導致的unexpected DEX problem異常。(2)加載修復的DEX。

整體的流程圖如下:

從流程圖來看,可以很明顯的找到這種方式的特點:

優勢:

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

2、可以實現類替換,兼容性高。(某些三星手機不起作用)

不足:

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

2.為了實現修復這個過程,必須在應用中加入兩個dex!dalvikhack.dex中只有一個類,對性能影響不大,但是對于patch.dex來說,修復的類到了一定數量,就需要花不少的時間加載。

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

2微信Tinker

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

從流程圖來看,同樣可以很明顯的找到這種方式的特點:

優勢:

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

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

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

不足:

1、與超級補丁技術一樣,不支持即時生效,必須通過重啟應用的方式才能生效。

2、需要給應用開啟新的進程才能進行合并,并且很容易因為內存消耗等原因合并失敗。

3、合并時占用額外磁盤空間,對于多DEX的應用來說,如果修改了多個DEX文件,就需要下發多個patch.dex與對應的classes.dex進行合并操作時這種情況會更嚴重,因此合并過程的失敗率也會更高。

小結

QQ空間超級補丁技術和微信Tinker的修復原理都基于類加載,在功能上已經支持類、資源的替換和新增,功能非常強大。但是也有非常多的問題。

A、多DEX帶來的性能問題和影響

多DEX方案用來解決應用方法數65k的問題,現在Google也官方支持了MultiDex的實現方案。但是,這實在是應用因方法數超出而作出的不得已的下策,超級補丁技術和Tinker作為一種熱修復的方案,平生給應用增加了多個DEX,而多DEX技術最大的問題在于性能上的坑,因此基于這種方案的補丁技術影響應用的性能是無疑的。

(a)啟動加載時間過長

可以看到,超級補丁技術和Tinker都選擇在Application的attachBaseContext()進行補丁dex的加載,即使這是加載dex的最佳時機,但是依然會帶來很大的性能問題,首當其沖的就是啟動時間太長。對于補丁DEX來說,應用啟動時虛擬機會將patch.dex文件轉換成odex文件,這個過程非常耗時。而這個過程,又要求需要在主線程中,以同步的方式執行,否則無法成功進行修復。就DEX的加載時間,大概做了以下的時間測試。

隨著patch.dex的增加,在不做任何優化的情況下,啟動時間也直線增長。

(b)易造成應用的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,就更加的得不償失。

如果我們僅僅就是開發一款app,沒有大改動,不會熱更全局變量,不會增加方法,那么AndFix框架是首選。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容