向每一個(gè)錯(cuò)誤致敬—— AndFix學(xué)習(xí)記錄

項(xiàng)目以飛快的速度迭代,2周進(jìn)行一次迭代升級(jí)炎滞。每次開(kāi)發(fā)完功能跑通愕掏,在現(xiàn)有機(jī)型上測(cè)試沒(méi)問(wèn)題的話奄薇,就提交市場(chǎng)。

在以極快的速度迭代的時(shí)候胆描,避免不了出現(xiàn)各種問(wèn)題——傳說(shuō)中的bug

或瞬雹,重大bug悄晃,需要緊急修復(fù)
或训唱,可以下次迭代修復(fù)的bug
或褥蚯,影響用戶體驗(yàn)的行為

出現(xiàn)bug后,我們的一貫做法:

發(fā)布緊急版本况增,等待用戶下載更新赞庶。

可是,并不是所有的用戶都會(huì)下載更新澳骤。那該怎么辦歧强?

強(qiáng)制更新,這是不太友好的行為为肮,如果人現(xiàn)在用的是流量摊册,強(qiáng)制別人更新,會(huì)耗費(fèi)別人流量颊艳,還是挺貴的茅特!這確實(shí)有點(diǎn)說(shuō)不過(guò)去。

有沒(méi)有一種方法棋枕,在用戶不下載新App的前提下修復(fù)這些bug白修。當(dāng)然有,以下是我找到的兩種解決方案重斑。

有兩種解決方案:

  1. Dexposed
  2. AndFix

##Dexposed

Dexposed是基于開(kāi)源Xposed框架實(shí)現(xiàn)的一個(gè)Android平臺(tái)上功能強(qiáng)大的無(wú)侵入式運(yùn)行時(shí)AOP框架兵睛。

Dexposed的AOP實(shí)現(xiàn)是完全無(wú)侵入式的,沒(méi)有使用任何注解處理器窥浪,編織器或者字節(jié)碼重寫(xiě)器祖很。只需要在應(yīng)用初始化階段加載一個(gè)很小的JNI庫(kù)就可以完成Dexposed框架的集成。

Dexposed不僅可以hook應(yīng)用中的自定義函數(shù)漾脂,也可以hook你的應(yīng)用進(jìn)程中調(diào)用的Android框架的函數(shù)突琳。這個(gè)特性對(duì)于Android開(kāi)發(fā)者來(lái)說(shuō)有很多好處,因?yàn)槲覀儑?yán)重依賴于Android SDK的版本碎片化符相。

借助動(dòng)態(tài)類加載技術(shù)拆融,運(yùn)行中的app可以加載一小段經(jīng)過(guò)編譯的Java AOP代碼,在不需要重啟app的前提下實(shí)現(xiàn)修改目標(biāo)app的行為啊终。

典型的應(yīng)用場(chǎng)景

AOP編程
插樁(例如測(cè)試镜豹,性能監(jiān)控等)
在線熱更新,修復(fù)嚴(yán)重的蓝牲,緊急的或者安全性的bug
SDK hooking以提供更好的開(kāi)發(fā)體驗(yàn)

在這里趟脂,我只關(guān)心Dexposed在Android平臺(tái)上的在線熱更新Hotfix的使用:

在Android平臺(tái),Dexposed支持函數(shù)級(jí)別的在線熱更新例衍,例如對(duì)已經(jīng)發(fā)布在應(yīng)用市場(chǎng)上的APK昔期,當(dāng)我們從crash統(tǒng)計(jì)平臺(tái)上發(fā)現(xiàn)某個(gè)函數(shù)調(diào)用有bug已卸,導(dǎo)致經(jīng)常性crash,而這個(gè)時(shí)候離下次迭代的預(yù)定發(fā)布日期還有一段時(shí)間硼一。這種情況下累澡,可以在本地開(kāi)發(fā)一個(gè)補(bǔ)丁APK,并發(fā)布到服務(wù)器中般贼,應(yīng)用市場(chǎng)上的APK下載這個(gè)補(bǔ)丁APK并集成后愧哟,就可以修復(fù)這個(gè)crash。

上述涉及到兩個(gè)角色:

  1. 宿主apk——已經(jīng)發(fā)布在應(yīng)用市場(chǎng)上哼蛆,有bug并且需要修復(fù)的apk;
  2. 補(bǔ)丁apk——在本地開(kāi)發(fā)的蕊梧,修復(fù)了bug的apk;

HotFix是什么

HotFix用來(lái)修復(fù)線上嚴(yán)重的,緊急的或者安全性的bug腮介,這里會(huì)涉及到兩個(gè)apk文件:一個(gè)我們稱為宿主apk肥矢,也就是發(fā)布到應(yīng)用市場(chǎng)的apk,一個(gè)稱為補(bǔ)丁apk叠洗。宿主apk出現(xiàn)bug時(shí)甘改,通過(guò)在線下載的方式從服務(wù)器下載補(bǔ)丁apk,使用補(bǔ)丁apk中的函數(shù)替換原來(lái)的函數(shù)惕味,從而實(shí)現(xiàn)在線修復(fù)bug的功能楼誓。

如何借助Dexposed實(shí)現(xiàn)HotFix

  1. 需要再引入一個(gè)名為patchloader的jar包,這個(gè)函數(shù)庫(kù)實(shí)現(xiàn)了一個(gè)熱更新框架玉锌,宿主apk在發(fā)布時(shí)會(huì)將這個(gè)jar包一起打包進(jìn)apk中
  2. 補(bǔ)丁apk只是在編譯時(shí)需要這個(gè)jar包名挥,但打包成apk時(shí)不包含這個(gè)jar包,以免補(bǔ)丁apk集成到宿主apk中時(shí)發(fā)生沖突
  3. 補(bǔ)丁apk將會(huì)以provided的形式依賴dexposedbridge.jar和patchloader.jar,補(bǔ)丁apk的build.gradle文件中依賴部分腳本如下所示
dependencies {
    provided files('libs/dexposedbridge.jar')
    provided files('libs/patchloader.jar')
}

支持狀態(tài):

Dexposed支持從Android2.3到4.4(除了3.0)的所有dalvid運(yùn)行時(shí)arm架構(gòu)的設(shè)備主守,穩(wěn)定性已經(jīng)經(jīng)過(guò)實(shí)踐檢驗(yàn)禀倔。

缺點(diǎn):

我的設(shè)備是5.0 或者更高系統(tǒng)的使用起來(lái)就有點(diǎn)問(wèn)題了。

因此参淫,我并沒(méi)打算仔細(xì)的學(xué)習(xí)Dexposed救湖,只是做一個(gè)簡(jiǎn)要的了解。

具體使用可以參見(jiàn)這個(gè)博客涎才,寫(xiě)的很棒:

使用示例:Android平臺(tái)免Root無(wú)侵入AOP框架Dexposed使用詳解

##AndFix

AndFix

AndFix is What ?

經(jīng)過(guò)綜合對(duì)比鞋既,發(fā)現(xiàn)AndFix可用性比較強(qiáng),因此決定學(xué)習(xí)AndFix的使用耍铜,從官方文檔入手邑闺。

AndFix是一個(gè)Android App的在線熱補(bǔ)丁框架。使用此框架棕兼,我們能夠在不重復(fù)發(fā)版的情況下陡舅,在線修改App中的Bug。AndFix就是 “Android Hot-Fix”的縮寫(xiě)伴挚。
它支持Android 2.3到6.0版本靶衍,并且支持arm與X86系統(tǒng)架構(gòu)的設(shè)備灾炭。完美支持Dalvik與ART的Runtime。AndFix 的補(bǔ)丁文件是以 .apatch 結(jié)尾的文件颅眶。它從你的服務(wù)器分發(fā)到你的客戶端來(lái)修復(fù)你App的bug 蜈出。

原理:

AndFix執(zhí)行的原理是實(shí)現(xiàn)方法體的替換,如下圖所示(該圖片來(lái)自AndFix github介紹):

principle.png

方法替換:

AndFix通過(guò)Java的自定義注解來(lái)判斷一個(gè)方法是否應(yīng)該被替換帚呼,如果可以就會(huì)hook該方法并進(jìn)行替換掏缎。AndFix在ART架構(gòu)上的Native方法是art_replaceMethod 、在X86架構(gòu)上的Native方法是dalvik_replaceMethod煤杀。他們的實(shí)現(xiàn)方式是不同的眷蜈。對(duì)于Dalvik,它將改變目標(biāo)方法的類型為Native同時(shí)hook方法的實(shí)現(xiàn)至AndFix自己的Native方法沈自,這個(gè)方法稱為 dalvik_dispatcher,這個(gè)方法將會(huì)喚醒已經(jīng)注冊(cè)的回調(diào)酌儒,這就是我們通常說(shuō)的hooked(掛鉤)。對(duì)于ART來(lái)說(shuō)枯途,我們僅僅改變目標(biāo)方法的屬性來(lái)替代它忌怎。

更多信息,見(jiàn)這里:AndFix/jni/

Fix 流程酪夷,大致如下:

  1. 發(fā)現(xiàn)bug(友盟榴啸、TestIn 、用戶反饋)
  2. 分析bug產(chǎn)生原因
  3. 創(chuàng)建并發(fā)布補(bǔ)丁
  4. App打補(bǔ)丁

在App中引入AndFix

如何來(lái)獲得AndFix 晚岭?

將AndFix aar 作為library編譯入你的工程:

  • 如果你用的是maven依賴鸥印,添加如下代碼:
<dependency>
    <groupId>com.alipay.euler</groupId>
    <artifactId>andfix</artifactId>
    <version>0.3.1</version>
    <type>aar</type>
</dependency>
  • 如果你用的是gradle依賴,添加如下代碼:
dependencies {
    compile 'com.alipay.euler:andfix:0.3.1@aar'
}

如何使用AndFix 坦报?

  1. 初始化 PatchManager
 patchManager = new PatchManager(context);
 patchManager.init(appversion);//current version
  1. Load patch 加載補(bǔ)丁
 patchManager.loadPatch();

你應(yīng)該盡可能早的加載補(bǔ)丁库说,通常都是在Application的onCreate()方法中進(jìn)行初始化。

  1. Add patch 添加補(bǔ)丁
 patchManager.addPatch(path);//path:補(bǔ)丁文件下載到本地的路徑片择。

當(dāng)一個(gè)新的補(bǔ)丁文件被下載后潜的,調(diào)用addPatch(path)就會(huì)立即生效。

開(kāi)發(fā)工具

AndFix 提供了一個(gè)補(bǔ)丁創(chuàng)建工具 apkpatch.

如何使用這個(gè)工具

1.準(zhǔn)備兩個(gè)應(yīng)用apk: 一個(gè)是線上的apk字管,另一個(gè)是修復(fù)了bug的apk.
2.通過(guò)提供的兩個(gè)apk生成補(bǔ)丁文件.

運(yùn)行如下命令:

usage: apkpatch -f <new> -t <old> -o <output> -k <keystore> -p <***> -a <alias> -e <***>
 -a,--alias <alias>     keystore entry alias.
 -e,--epassword <***>   keystore entry password.
 -f,--from <loc>        new Apk file path.
 -k,--keystore <loc>    keystore path.
 -n,--name <name>       patch name.
 -o,--out <dir>         output dir.
 -p,--kpassword <***>   keystore password.
 -t,--to <loc>          old Apk file path.

這個(gè)時(shí)候啰挪,你就有了你的應(yīng)用的救世主——補(bǔ)丁文件。接下來(lái)你就可以通過(guò)某種方式將該補(bǔ)丁文件分發(fā)給你的客戶端嘲叔。

  • 或亡呵,adb push 到sdk
  • 或,發(fā)布到服務(wù)器借跪,客戶端下載該apatch

有時(shí)候政己,你的團(tuán)隊(duì)成員可能會(huì)fix各自的bug,這個(gè)時(shí)候就會(huì)有不止一個(gè)補(bǔ)丁文件.apatch。這種情況下,你可以用這個(gè)工具merge這些.apatch文件:

usage: apkpatch -m <apatch_path...> -o <output> -k <keystore> -p <***> -a <alias> -e <***>
 -a,--alias <alias>     keystore entry alias.
 -e,--epassword <***>   keystore entry password.
 -k,--keystore <loc>    keystore path.
 -m,--merge <loc...>    path of .apatch files.
 -n,--name <name>       patch name.
 -o,--out <dir>         output dir.
 -p,--kpassword <***>   keystore password.

運(yùn)行示例

  1. 導(dǎo)入samplesI/AndFixDemo進(jìn)你的IDE歇由,給AndFixDemo添加AndFix(library project 或者 aar)的依賴卵牍。
  2. build 工程,保存應(yīng)用為1.apk沦泌,然后將該apk安裝到手機(jī)或者模擬器上糊昙。
  3. 修改com.euler.test.A,作為Fix后的包谢谦。
  4. build 工程释牺,保存應(yīng)用為2.apk
  5. Use apkpatch tool to make a patch. 使用apkpatch 工具制作一個(gè)補(bǔ)丁文件。
  6. Rename the patch file to out.apatch, and then copy it to sdcard.重命名補(bǔ)丁文件為out.apatch 回挽,然后將它拷貝到sdcard上没咙。
  7. 運(yùn)行1.apk然后查看log

注意

混淆

如果你使用混淆,你要保存mapping.txt千劈,這樣的話你在新版本的構(gòu)建是就可以借助 “"-applymapping” 來(lái)使用它了祭刚。

混淆時(shí)需要保留下面的內(nèi)容:

  • Native方法 ,比如:com.alipay.euler.andfix.AndFix
  • Annotation墙牌,比如:com.alipay.euler.andfix.annotation.MethodReplace

為了確保這些類在運(yùn)行了ProGuard后涡驮,可以被找見(jiàn),在ProGuard配置中添加如下代碼:

-keep class * extends java.lang.annotation.Annotation
-keepclasseswithmembernames class * {
    native <methods>;
}

局限性

如果你使用了喜滨,軟件加固比如 Bangcle捉捅,為了生成補(bǔ)丁文件,你最好使用為經(jīng)過(guò)加固的apk.使用這些加固虽风,很可能使熱補(bǔ)丁失效棒口。

安全性

驗(yàn)證apatch文件的簽名是否就是在使用apkpatch工具時(shí)使用的簽名(如果不驗(yàn)證那么任何人都可以制作自己的apatch文件來(lái)對(duì)你的APP進(jìn)行修改)

驗(yàn)證optimize file的指紋(防止有人替換掉本地保存的補(bǔ)丁文件,所以要驗(yàn)證MD5碼)


以下是我在用demo時(shí)碰到的問(wèn)題:

  • 運(yùn)行apkpatch報(bào)nullPointException

關(guān)于該問(wèn)題的github issue : 運(yùn)行apkpatch報(bào)nullPointException

解決方法:

運(yùn)行命令行時(shí) alias 填錯(cuò)了焰情,應(yīng)該填alias名字陌凳,我填成了alias路徑剥懒。修改后内舟,解決了問(wèn)題。

zhanggeng:apkpatch-1.0.3$
./apkpatch.sh 
-f /Users/zhanggeng/Desktop/after/app-release.apk 
-t /Users/zhanggeng/Desktop/before/app-release.apk 
-o /Users/zhanggeng/Desktop/apatch/ 
-k /Users/zhanggeng/Desktop/fixdemo.jks
-p 123456 
-a fixdemo 
-e 123456
  • 運(yùn)行程序初橘,檢測(cè)是否可以使用時(shí)验游,拋出異常:java.util.zip.ZipException: File too short to be a zip file: 0

關(guān)于該問(wèn)題的github issue: java.util.zip.ZipException: File too short to be a zip file: 0

解決方法:

這個(gè)問(wèn)題是,上面那個(gè)問(wèn)題造成的保檐。只要補(bǔ)丁文件生成正確耕蝉,是不會(huì)有這個(gè)問(wèn)題的。

參考文章:


Demo下載地址:AndFixDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末夜只,一起剝皮案震驚了整個(gè)濱河市垒在,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扔亥,老刑警劉巖场躯,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谈为,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡踢关,警方通過(guò)查閱死者的電腦和手機(jī)伞鲫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)签舞,“玉大人秕脓,你說(shuō)我怎么就攤上這事∪宕睿” “怎么了吠架?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)搂鲫。 經(jīng)常有香客問(wèn)我诵肛,道長(zhǎng),這世上最難降的妖魔是什么默穴? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任怔檩,我火速辦了婚禮,結(jié)果婚禮上蓄诽,老公的妹妹穿的比我還像新娘薛训。我一直安慰自己,他們只是感情好仑氛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布乙埃。 她就那樣靜靜地躺著,像睡著了一般锯岖。 火紅的嫁衣襯著肌膚如雪介袜。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天出吹,我揣著相機(jī)與錄音遇伞,去河邊找鬼。 笑死捶牢,一個(gè)胖子當(dāng)著我的面吹牛鸠珠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秋麸,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼渐排,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了灸蟆?” 一聲冷哼從身側(cè)響起驯耻,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后可缚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體孽水,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年城看,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了女气。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡测柠,死狀恐怖炼鞠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情轰胁,我是刑警寧澤谒主,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站赃阀,受9級(jí)特大地震影響霎肯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜榛斯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一观游、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧驮俗,春花似錦懂缕、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至索烹,卻和暖如春工碾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背百姓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工渊额, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瓣戚。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓端圈,卻偏偏與公主長(zhǎng)得像焦读,于是被迫代替她去往敵國(guó)和親子库。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,095評(píng)論 25 707
  • 熱更新相關(guān)概念 組件化---就是將一個(gè)app分成多個(gè)模塊矗晃,每個(gè)模塊都是一個(gè)組件(Module)仑嗅,開(kāi)發(fā)的過(guò)程中我們可...
    wangling90閱讀 1,789評(píng)論 0 0
  • 2015年以來(lái),Android開(kāi)發(fā)領(lǐng)域里對(duì)熱修復(fù)技術(shù)的討論和分享越來(lái)越多,同時(shí)也出現(xiàn)了一些不同的解決方案仓技,如QQ空...
    日月星辰_9e1a閱讀 5,724評(píng)論 3 20
  • 今天下午上第三節(jié)課的時(shí)候鸵贬,學(xué)校的廣播響了起來(lái),廣播里傳來(lái)了德肓處老師的聲音脖捻,請(qǐng)四五年級(jí)的班主任老師帶幾名同學(xué)到一樓...
    五三胡連海閱讀 248評(píng)論 0 0
  • 去屑就用海飛絲不傷手的XXX怕上火就喝XXX傷口怕感染快用XXX………… 廣告是如何讓你產(chǎn)生行動(dòng)的阔逼?一則廣告播出,...
    傻子快跑閱讀 259評(píng)論 0 1