Andfix熱修復(fù)技術(shù)淺析

By吳思博

一、什么是熱修復(fù):

二、技術(shù)背景

三、當(dāng)前主流的熱修復(fù)方案比較

四、Andfix的介紹與實(shí)踐(開(kāi)源)

1原理

2基本使用

五、其他方案介紹

一、什么是熱修復(fù):

熱修復(fù)(HotFix)是以補(bǔ)丁的方式動(dòng)態(tài)修復(fù)緊急Bug,不再需要重新發(fā)布App,不需要用戶重新下載覆蓋安裝的方式來(lái)實(shí)現(xiàn)代碼的替換修改。

二、技術(shù)背景

(1)正常開(kāi)發(fā)流程

從流程來(lái)看,傳統(tǒng)的開(kāi)發(fā)流程存在很多弊端:

1、重新發(fā)布版本代價(jià)太大

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

3、BUG修復(fù)不及時(shí),用戶體驗(yàn)太差

(2)熱修復(fù)開(kāi)發(fā)流程

而熱修復(fù)的開(kāi)發(fā)流程顯得更加靈活,優(yōu)勢(shì)很多:

1、無(wú)需重新發(fā)版,實(shí)時(shí)高效修復(fù)

2、用戶無(wú)感知修復(fù),無(wú)需下載新的應(yīng)用,代價(jià)小

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

三、當(dāng)前主流的熱修復(fù)方案比較:

熱修復(fù)技術(shù)近期變得越來(lái)越熱門,同時(shí)也出現(xiàn)了一些不同的解決方案,如阿里的AndFix(開(kāi)源)、QQ空間補(bǔ)丁方案、以及微信的Tinker方案,但是它們的原理、適用場(chǎng)景都各有不同。我們項(xiàng)目中可以采用哪種方案,是我們比較關(guān)注的問(wèn)題。

QQ空間超級(jí)補(bǔ)丁技術(shù)和微信Tinker支持新增類和資源的替換,在一些功能化的更新上更為強(qiáng)大,但對(duì)應(yīng)用的性能和穩(wěn)定會(huì)有的一定的影響;AndFix雖然暫時(shí)不支持新增類和資源的替換,對(duì)新功能的發(fā)布也有所限制,作為定位為線上緊急BUG的熱修復(fù)的服務(wù)來(lái)說(shuō),還是比較好的,同時(shí)對(duì)應(yīng)用性能不產(chǎn)生不必要的損耗,在熱修復(fù)方面不失為一個(gè)好的選擇。

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

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

(1)AndFix是一個(gè)Android App的在線熱補(bǔ)丁框架。使用此框架,我們能夠在不重復(fù)發(fā)版的情況下,在線修改App中的Bug。AndFix就是“android Hot-Fix”的縮寫。就目前來(lái)說(shuō),AndFix支持Android 2.3到7.0版本,并且支持arm與X86系統(tǒng)架構(gòu)的設(shè)備。支持Dalvik與ART的Runtime。AndFix的補(bǔ)丁文件是以.apatch結(jié)尾的文件。

AndFix不同于QQ空間超級(jí)補(bǔ)丁技術(shù)和微信Tinker通過(guò)增加或替換整個(gè)DEX的方案,提供了一種運(yùn)行時(shí)在Native修改Filed指針的方式,實(shí)現(xiàn)方法的替換,達(dá)到即時(shí)生效無(wú)需重啟,對(duì)應(yīng)用無(wú)性能消耗

AndFix實(shí)現(xiàn)過(guò)程:

對(duì)于實(shí)現(xiàn)方法的替換,在Native層操作,經(jīng)過(guò)三個(gè)步驟:

下面以Dalvik設(shè)備為例,來(lái)分析具體的實(shí)現(xiàn)過(guò)程

對(duì)于Dalvik來(lái)說(shuō),遵循JIT即時(shí)編譯機(jī)制,需要在運(yùn)行時(shí)裝載libdvm.so動(dòng)態(tài)庫(kù),獲取以下內(nèi)部函數(shù):

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

2 dvmDecodeIndirectRef():根據(jù)當(dāng)前線程獲得ClassObject對(duì)象。

setFieldFlag

動(dòng)態(tài)庫(kù)會(huì)忽略非public屬性的字段和方法,該操作的目的:讓private、protected的方法和字段可被動(dòng)態(tài)庫(kù)看見(jiàn)并識(shí)別。

replaceMethod:

AndFix對(duì)ART設(shè)備同樣支持,具體的過(guò)程與Dalvik相似。

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

優(yōu)點(diǎn):

1、BUG修復(fù)的即時(shí)

2、補(bǔ)丁包同樣采用差量技術(shù),生成的PATCH體積小

3、對(duì)應(yīng)用無(wú)侵入,幾乎無(wú)性能損耗

不足:

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

2、由于廠商的自定義ROM,對(duì)少數(shù)機(jī)型可能暫時(shí)不支持。

(2)Andfix的基本使用。

1.在自定義Application中初始化,為了更早的修復(fù)應(yīng)用中的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");

//加載已經(jīng)添加到PatchManager中的patch

mPatchManager.loadPatch();

}

}

2.如果有新的補(bǔ)丁需要修復(fù),下載完成后,進(jìn)行以下操作

//添加patch,只需指定patch的路徑即可,補(bǔ)丁會(huì)立即生效

mPatchManager.addPatch(path);

3.當(dāng)apk版本升級(jí),需要把之前patch文件的刪除,需要以下操作

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

mPatchManager.removeAllPatch();

我們也可封裝成一個(gè)工具類

patch文件的生成

使用工具:apkpatch-1.0.3

原理:根據(jù)兩個(gè)apk包,生成一個(gè)差異文件,就是所謂的補(bǔ)丁文件即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用戶別名密碼

執(zhí)行完命令,就會(huì)在輸出目錄中輸出.apatch文件:

new-c293df7dbc23f11214fdd020ea78d3b8.apatch

:就是patch文件。

.apatch文件根目錄內(nèi)容:

META_INF

文件下內(nèi)容:

PATCH.MF

文件內(nèi)容:

注:Patch-Classes就是改動(dòng)過(guò)的class.

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

在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、如何解決某些機(jī)型不兼容的問(wèn)題?還有采用YunOS系統(tǒng)的?之前測(cè)試在ART模式機(jī)型上,偶現(xiàn)崩潰,不知采用了什么機(jī)制處理的?線上采用Andfix,會(huì)不會(huì)風(fēng)險(xiǎn)比較高?

ART的機(jī)型上暫時(shí)未出現(xiàn)崩潰的現(xiàn)象。對(duì)于機(jī)型的兼容性問(wèn)題,采取的措施是做平臺(tái)區(qū)間控制基本兼容,對(duì)于個(gè)別機(jī)型的問(wèn)題,可以參考統(tǒng)計(jì)數(shù)據(jù)加入黑名單,與服務(wù)器約定協(xié)議,黑名單上的機(jī)型不走andifx流程。美聊線上實(shí)踐暫時(shí)未出現(xiàn)不可控的問(wèn)題

2、沒(méi)有成功更新的情況下,會(huì)不會(huì)引起app的崩潰?

更新不成功不會(huì)引起崩潰

五、其他技術(shù)簡(jiǎn)介

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

PathClassLoader用來(lái)加載系統(tǒng)類和應(yīng)用類。DexClassLoader用來(lái)加載jar、apk、dex文件.加載jar、apk也是最終抽取里面的Dex文件進(jìn)行加載。

1、QQ空間超級(jí)補(bǔ)丁技術(shù)

QQ空間超級(jí)補(bǔ)丁技術(shù)基于DEX分包方案,使用了多DEX加載的原理,大致的過(guò)程就是:把BUG方法修復(fù)以后,放到一個(gè)單獨(dú)的DEX里,插入到dexElements數(shù)組的最前面,讓虛擬機(jī)去加載修復(fù)完后的方法。

當(dāng)patch.dex中包含A.class時(shí)就會(huì)優(yōu)先加載,在后續(xù)的DEX中遇到A.class的話就會(huì)直接返回而不去加載,這樣就達(dá)到了修復(fù)的目的。

但是有一個(gè)問(wèn)題是,當(dāng)兩個(gè)調(diào)用關(guān)系的類在同一個(gè)DEX時(shí),就會(huì)產(chǎn)生異常報(bào)錯(cuò)。在APK安裝時(shí),虛擬機(jī)需要將classes.dex優(yōu)化成odex文件,然后才會(huì)執(zhí)行。在這個(gè)過(guò)程中,會(huì)進(jìn)行類的verify操作,如果方法中直接引用到的類(第一層級(jí)關(guān)系,不會(huì)進(jìn)行遞歸搜索)和class都在同一個(gè)dex中的話,那么這個(gè)類就會(huì)被打上CLASS_ISPREVERIFIED,然后才會(huì)寫入odex文件。所以,為了可以正常的進(jìn)行打補(bǔ)丁修復(fù),必須避免類被打上CLASS_ISPREVERIFIED標(biāo)志,具體的做法就是單獨(dú)放一個(gè)類在另外DEX中,讓其他類調(diào)用。

其實(shí)就是兩件事:

1、動(dòng)態(tài)改變BaseDexClassLoader對(duì)象間接引用的dexElements;

2、在app打包的時(shí)候,阻止相關(guān)類去打上CLASS_ISPREVERIFIED標(biāo)志。

手機(jī)QQ空間APK具體的實(shí)現(xiàn):先進(jìn)入程序入口QZoneRealApplication,在attachBaseContext中進(jìn)行了兩步操作:(1)修復(fù)CLASS_ISPREVERIFIED標(biāo)志導(dǎo)致的unexpected DEX problem異常。(2)加載修復(fù)的DEX。

整體的流程圖如下:

從流程圖來(lái)看,可以很明顯的找到這種方式的特點(diǎn):

優(yōu)勢(shì):

1、沒(méi)有合成整包(和微信Tinker比起來(lái)),產(chǎn)物比較小,比較靈活

2、可以實(shí)現(xiàn)類替換,兼容性高。(某些三星手機(jī)不起作用)

不足:

1.不支持即時(shí)生效,必須通過(guò)重啟才能生效。

2.為了實(shí)現(xiàn)修復(fù)這個(gè)過(guò)程,必須在應(yīng)用中加入兩個(gè)dex!dalvikhack.dex中只有一個(gè)類,對(duì)性能影響不大,但是對(duì)于patch.dex來(lái)說(shuō),修復(fù)的類到了一定數(shù)量,就需要花不少的時(shí)間加載。

3.在ART模式下,如果類修改了結(jié)構(gòu),就會(huì)出現(xiàn)內(nèi)存錯(cuò)亂的問(wèn)題。為了解決這個(gè)問(wèn)題,就必須把所有相關(guān)的調(diào)用類、父類子類等等全部加載到patch.dex中,導(dǎo)致補(bǔ)丁包異常的大,進(jìn)一步增加應(yīng)用啟動(dòng)加載的時(shí)候,耗時(shí)更加嚴(yán)重。

2微信Tinker

微信針對(duì)QQ空間超級(jí)補(bǔ)丁技術(shù)的不足提出了一個(gè)提供DEX差量包,整體替換DEX的方案。主要的原理是與QQ空間超級(jí)補(bǔ)丁技術(shù)基本相同,區(qū)別在于不再將patch.dex增加到elements數(shù)組中,而是差量的方式給出patch.dex,然后將patch.dex與應(yīng)用的classes.dex合并,然后整體替換掉舊的DEX,達(dá)到修復(fù)的目的。

從流程圖來(lái)看,同樣可以很明顯的找到這種方式的特點(diǎn):

優(yōu)勢(shì):

1、合成整包,不用在構(gòu)造函數(shù)插入代碼,防止verify,verify和opt在編譯期間就已經(jīng)完成,不會(huì)在運(yùn)行期間進(jìn)行、

2、性能提高。兼容性和穩(wěn)定性比較高。

3、開(kāi)發(fā)者透明,不需要對(duì)包進(jìn)行額外處理。

不足:

1、與超級(jí)補(bǔ)丁技術(shù)一樣,不支持即時(shí)生效,必須通過(guò)重啟應(yīng)用的方式才能生效。

2、需要給應(yīng)用開(kāi)啟新的進(jìn)程才能進(jìn)行合并,并且很容易因?yàn)閮?nèi)存消耗等原因合并失敗。

3、合并時(shí)占用額外磁盤空間,對(duì)于多DEX的應(yīng)用來(lái)說(shuō),如果修改了多個(gè)DEX文件,就需要下發(fā)多個(gè)patch.dex與對(duì)應(yīng)的classes.dex進(jìn)行合并操作時(shí)這種情況會(huì)更嚴(yán)重,因此合并過(guò)程的失敗率也會(huì)更高。

小結(jié)

QQ空間超級(jí)補(bǔ)丁技術(shù)和微信Tinker的修復(fù)原理都基于類加載,在功能上已經(jīng)支持類、資源的替換和新增,功能非常強(qiáng)大。但是也有非常多的問(wèn)題。

A、多DEX帶來(lái)的性能問(wèn)題和影響

多DEX方案用來(lái)解決應(yīng)用方法數(shù)65k的問(wèn)題,現(xiàn)在Google也官方支持了MultiDex的實(shí)現(xiàn)方案。但是,這實(shí)在是應(yīng)用因方法數(shù)超出而作出的不得已的下策,超級(jí)補(bǔ)丁技術(shù)和Tinker作為一種熱修復(fù)的方案,平生給應(yīng)用增加了多個(gè)DEX,而多DEX技術(shù)最大的問(wèn)題在于性能上的坑,因此基于這種方案的補(bǔ)丁技術(shù)影響應(yīng)用的性能是無(wú)疑的。

(a)啟動(dòng)加載時(shí)間過(guò)長(zhǎng)

可以看到,超級(jí)補(bǔ)丁技術(shù)和Tinker都選擇在Application的attachBaseContext()進(jìn)行補(bǔ)丁dex的加載,即使這是加載dex的最佳時(shí)機(jī),但是依然會(huì)帶來(lái)很大的性能問(wèn)題,首當(dāng)其沖的就是啟動(dòng)時(shí)間太長(zhǎng)。對(duì)于補(bǔ)丁DEX來(lái)說(shuō),應(yīng)用啟動(dòng)時(shí)虛擬機(jī)會(huì)將patch.dex文件轉(zhuǎn)換成odex文件,這個(gè)過(guò)程非常耗時(shí)。而這個(gè)過(guò)程,又要求需要在主線程中,以同步的方式執(zhí)行,否則無(wú)法成功進(jìn)行修復(fù)。就DEX的加載時(shí)間,大概做了以下的時(shí)間測(cè)試。

隨著patch.dex的增加,在不做任何優(yōu)化的情況下,啟動(dòng)時(shí)間也直線增長(zhǎng)。

(b)易造成應(yīng)用的ANR和Crash

正是尤其多DEX加載導(dǎo)致了啟動(dòng)時(shí)間過(guò)長(zhǎng),很容易就會(huì)引發(fā)應(yīng)用的ANR。我們知道當(dāng)應(yīng)用在主線程等待超過(guò)5s以后,就會(huì)直接導(dǎo)致長(zhǎng)時(shí)間無(wú)響應(yīng)而退出。超級(jí)補(bǔ)丁技術(shù)為保證ART不出現(xiàn)地址錯(cuò)亂問(wèn)題,需要將所有關(guān)聯(lián)的類全部加入到補(bǔ)丁中,而微信Tinker采取一種差量包合并加載的方式,都會(huì)使要加載的dex體積變得很大。這也很大程度上容易導(dǎo)致ANR情況的出現(xiàn)。

除了應(yīng)用ANR以外,多DEX模式也同樣很容易導(dǎo)致Crash情況的出現(xiàn)。我們知道,超級(jí)補(bǔ)丁技術(shù)為了保證ART設(shè)備下不出現(xiàn)地址錯(cuò)亂問(wèn)題,需要把修改類的所有相關(guān)類全部加入到補(bǔ)丁中,這里會(huì)出現(xiàn)一個(gè)問(wèn)題,為了保證補(bǔ)丁包的體積最小,能否保證引入全部的關(guān)聯(lián)類而不引入無(wú)關(guān)的類呢?一旦沒(méi)有引入關(guān)聯(lián)的類,就會(huì)出現(xiàn)以下的異常:

NoClassDefFoundError

Could not find class

Could not find method

出現(xiàn)這些異常,就會(huì)直接導(dǎo)致應(yīng)用的Crash退出。所以,不難看出如果我們需要修復(fù)一個(gè)不是Crash的BUG,但是因?yàn)槲醇尤胂嚓P(guān)類而導(dǎo)致了更嚴(yán)重的Crash,就更加的得不償失。

如果我們僅僅就是開(kāi)發(fā)一款app,沒(méi)有大改動(dòng),不會(huì)熱更全局變量,不會(huì)增加方法,那么AndFix框架是首選。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容