安卓App熱補(bǔ)丁動(dòng)態(tài)修復(fù)技術(shù)介紹

1.背景

當(dāng)一個(gè)App發(fā)布之后,突然發(fā)現(xiàn)了一個(gè)嚴(yán)重bug需要進(jìn)行緊急修復(fù)斯棒,這時(shí)候公司各方就會(huì)忙得焦頭爛額:重新打包App盾致、測試、向各個(gè)應(yīng)用市場和渠道換包荣暮、提示用戶升級庭惜、用戶下載、覆蓋安裝穗酥。有時(shí)候僅僅是為了修改了一行代碼护赊,也要付出巨大的成本進(jìn)行換包和重新發(fā)布。

這時(shí)候就提出一個(gè)問題:有沒有辦法以補(bǔ)丁的方式動(dòng)態(tài)修復(fù)緊急Bug迷扇,不再需要重新發(fā)布App百揭,不再需要用戶重新下載爽哎,覆蓋安裝蜓席?

雖然Android系統(tǒng)并沒有提供這個(gè)技術(shù),但是很幸運(yùn)的告訴大家课锌,答案是:可以厨内,我們QQ空間提出了熱補(bǔ)丁動(dòng)態(tài)修復(fù)技術(shù)來解決以上這些問題。

2.實(shí)際案例

空間Android獨(dú)立版5.2發(fā)布后渺贤,收到用戶反饋雏胃,結(jié)合版無法跳轉(zhuǎn)到獨(dú)立版的訪客界面,每天都較大的反饋志鞍。在以前只能緊急換包瞭亮,重新發(fā)布。成本非常高固棚,也影響用戶的口碑统翩。最終決定使用熱補(bǔ)丁動(dòng)態(tài)修復(fù)技術(shù)仙蚜,向用戶下發(fā)Patch,在用戶無感知的情況下厂汗,修復(fù)了外網(wǎng)問題委粉,取得非常好的效果。

3.解決方案

該方案基于的是android dex分包方案的娶桦,關(guān)于dex分包方案贾节,網(wǎng)上有幾篇解釋了,所以這里就不再贅述衷畦,具體可以看這里https://m.oschina.net/blog/308583栗涂。

簡單的概括一下,就是把多個(gè)dex文件塞入到app的classloader之中霎匈,但是android dex拆包方案中的類是沒有重復(fù)的戴差,如果classes.dex和classes1.dex中有重復(fù)的類,當(dāng)用到這個(gè)重復(fù)的類的時(shí)候铛嘱,系統(tǒng)會(huì)選擇哪個(gè)類進(jìn)行加載呢暖释?

讓我們來看看類加載的代碼:

一個(gè)ClassLoader可以包含多個(gè)dex文件,每個(gè)dex文件是一個(gè)Element墨吓,多個(gè)dex文件排列成一個(gè)有序的數(shù)組dexElements球匕,當(dāng)找類的時(shí)候,會(huì)按順序遍歷dex文件帖烘,然后從當(dāng)前遍歷的dex文件中找類亮曹,如果找類則返回,如果找不到從下一個(gè)dex文件繼續(xù)查找秘症。

理論上照卦,如果在不同的dex中有相同的類存在,那么會(huì)優(yōu)先選擇排在前面的dex文件的類乡摹,如下圖:

在此基礎(chǔ)上役耕,我們構(gòu)想了熱補(bǔ)丁的方案,把有問題的類打包到一個(gè)dex(patch.dex)中去聪廉,然后把這個(gè)dex插入到Elements的最前面瞬痘,如下圖:

好,該方案基于第二個(gè)拆分dex的方案板熊,方案實(shí)現(xiàn)如果懂拆分dex的原理的話框全,大家應(yīng)該很快就會(huì)實(shí)現(xiàn)該方案,如果沒有拆分dex的項(xiàng)目的話干签,可以參考一下谷歌的multidex方案實(shí)現(xiàn)津辩。然后在插入數(shù)組的時(shí)候,把補(bǔ)丁包插入到最前面去。

好喘沿,看似問題很簡單情萤,輕松的搞定了,讓我們來試驗(yàn)一下摹恨,修改某個(gè)類筋岛,然后打包成dex,插入到classloader晒哄,當(dāng)加載類的時(shí)候出現(xiàn)了(本例中是QzoneActivityManager要被替換):

為什么會(huì)出現(xiàn)以上問題呢睁宰?

從log的意思上來講,ModuleManager引用了QzoneActivityManager寝凌,但是發(fā)現(xiàn)這這兩個(gè)類所在的dex不在一起柒傻,其中:

1. ModuleManager在classes.dex中

2. QzoneActivityManager在patch.dex中

結(jié)果發(fā)生了錯(cuò)誤。

這里有個(gè)問題,拆分dex的很多類都不是在同一個(gè)dex內(nèi)的,怎么沒有問題?

讓我們搜索一下拋出錯(cuò)誤的代碼所在较木,嘿咻嘿咻红符,找到了一下代碼:

從代碼上來看,如果兩個(gè)相關(guān)聯(lián)的類在不同的dex中就會(huì)報(bào)錯(cuò)伐债,但是拆分dex沒有報(bào)錯(cuò)這是為什么预侯,原來這個(gè)校驗(yàn)的前提是:

如果引用者(也就是ModuleManager)這個(gè)類被打上了CLASS_ISPREVERIFIED標(biāo)志,那么就會(huì)進(jìn)行dex的校驗(yàn)峰锁。那么這個(gè)標(biāo)志是什么時(shí)候被打上去的萎馅?讓我們在繼續(xù)搜索一下代碼,嘿咻嘿咻~~虹蒋,在DexPrepare.cpp找到了一下代碼:

這段代碼是dex轉(zhuǎn)化成odex(dexopt)的代碼中的一段糜芳,我們知道當(dāng)一個(gè)apk在安裝的時(shí)候,apk中的classes.dex會(huì)被虛擬機(jī)(dexopt)優(yōu)化成odex文件魄衅,然后才會(huì)拿去執(zhí)行峭竣。

虛擬機(jī)在啟動(dòng)的時(shí)候,會(huì)有許多的啟動(dòng)參數(shù)晃虫,其中一項(xiàng)就是verify選項(xiàng)皆撩,當(dāng)verify選項(xiàng)被打開的時(shí)候,上面doVerify變量為true傲茄,那么就會(huì)執(zhí)行dvmVerifyClass進(jìn)行類的校驗(yàn)毅访,如果dvmVerifyClass校驗(yàn)類成功沮榜,那么這個(gè)類會(huì)被打上CLASS_ISPREVERIFIED的標(biāo)志盘榨,那么具體的校驗(yàn)過程是什么樣子的呢?

此代碼在DexVerify.cpp中蟆融,如下:

1. 驗(yàn)證clazz->directMethods方法草巡,directMethods包含了以下方法:

1. static方法

2. private方法

3. 構(gòu)造函數(shù)

2.clazz->virtualMethods

1. 虛函數(shù)=override方法?

概括一下就是如果以上方法中直接引用到的類(第一層級關(guān)系,不會(huì)進(jìn)行遞歸搜索)和clazz都在同一個(gè)dex中的話型酥,那么這個(gè)類就會(huì)被打上CLASS_ISPREVERIFIED:

所以為了實(shí)現(xiàn)補(bǔ)丁方案山憨,所以必須從這些方法中入手查乒,防止類被打上CLASS_ISPREVERIFIED標(biāo)志。

最終空間的方案是往所有類的構(gòu)造函數(shù)里面插入了一段代碼郁竟,代碼如下:

if (ClassVerifier.PREVENT_VERIFY) {

System.out.println(AntilazyLoad.class);

}

其中AntilazyLoad類會(huì)被打包成單獨(dú)的hack.dex玛迄,這樣當(dāng)安裝apk的時(shí)候,classes.dex內(nèi)的類都會(huì)引用一個(gè)在不相同dex中的AntilazyLoad類棚亩,這樣就防止了類被打上CLASS_ISPREVERIFIED的標(biāo)志了蓖议,只要沒被打上這個(gè)標(biāo)志的類都可以進(jìn)行打補(bǔ)丁操作。

然后在應(yīng)用啟動(dòng)的時(shí)候加載進(jìn)來.AntilazyLoad類所在的dex包必須被先加載進(jìn)來,不然AntilazyLoad類會(huì)被標(biāo)記為不存在讥蟆,即使后續(xù)加載了hack.dex包勒虾,那么他也是不存在的,這樣屏幕就會(huì)出現(xiàn)茫茫多的類AntilazyLoad找不到的log瘸彤。

所以Application作為應(yīng)用的入口不能插入這段代碼修然。(因?yàn)檩d入hack.dex的代碼是在Application中onCreate中執(zhí)行的,如果在Application的構(gòu)造函數(shù)里面插入了這段代碼质况,那么就是在hack.dex加載之前就使用該類愕宋,該類一次找不到,會(huì)被永遠(yuǎn)的打上找不到的標(biāo)志)

其中:

之所以選擇構(gòu)造函數(shù)是因?yàn)樗辉黾臃椒〝?shù)结榄,一個(gè)類即使沒有顯式的構(gòu)造函數(shù)掏婶,也會(huì)有一個(gè)隱式的默認(rèn)構(gòu)造函數(shù)。

空間使用的是在字節(jié)碼插入代碼,而不是源代碼插入潭陪,使用的是javaassist庫來進(jìn)行字節(jié)碼插入的雄妥。

隱患:

虛擬機(jī)在安裝期間為類打上CLASS_ISPREVERIFIED標(biāo)志是為了提高性能的,我們強(qiáng)制防止類被打上標(biāo)志是否會(huì)影響性能依溯?這里我們會(huì)做一下更加詳細(xì)的性能測試.但是在大項(xiàng)目中拆分dex的問題已經(jīng)比較嚴(yán)重老厌,很多類都沒有被打上這個(gè)標(biāo)志。

如何打包補(bǔ)丁包:

1. 空間在正式版本發(fā)布的時(shí)候黎炉,會(huì)生成一份緩存文件枝秤,里面記錄了所有class文件的md5,還有一份mapping混淆文件慷嗜。

2. 在后續(xù)的版本中使用-applymapping選項(xiàng)淀弹,應(yīng)用正式版本的mapping文件,然后計(jì)算編譯完成后的class文件的md5和正式版本進(jìn)行比較庆械,把不相同的class文件打包成補(bǔ)丁包薇溃。

備注:該方案現(xiàn)在也應(yīng)用到我們的編譯過程當(dāng)中,編譯不需要重新打包dex,只需要把修改過的類的class文件打包成patch dex,然后放到sdcard下,那么就會(huì)讓改變的代碼生效。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缭乘,一起剝皮案震驚了整個(gè)濱河市沐序,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖策幼,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邑时,死亡現(xiàn)場離奇詭異,居然都是意外死亡特姐,警方通過查閱死者的電腦和手機(jī)晶丘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唐含,“玉大人铣口,你說我怎么就攤上這事【鹾” “怎么了脑题?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铜靶。 經(jīng)常有香客問我叔遂,道長,這世上最難降的妖魔是什么争剿? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任已艰,我火速辦了婚禮,結(jié)果婚禮上蚕苇,老公的妹妹穿的比我還像新娘哩掺。我一直安慰自己,他們只是感情好涩笤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布嚼吞。 她就那樣靜靜地躺著,像睡著了一般蹬碧。 火紅的嫁衣襯著肌膚如雪舱禽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天恩沽,我揣著相機(jī)與錄音誊稚,去河邊找鬼。 笑死罗心,一個(gè)胖子當(dāng)著我的面吹牛里伯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播渤闷,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼疾瓮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肤晓?” 一聲冷哼從身側(cè)響起爷贫,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎补憾,沒想到半個(gè)月后漫萄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盈匾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年腾务,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片削饵。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡岩瘦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出窿撬,到底是詐尸還是另有隱情启昧,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布劈伴,位于F島的核電站密末,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏跛璧。R本人自食惡果不足惜严里,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望追城。 院中可真熱鬧刹碾,春花似錦、人聲如沸座柱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽色洞。三九已至瞬矩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锋玲,已是汗流浹背景用。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惭蹂,地道東北人伞插。 一個(gè)月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像盾碗,于是被迫代替她去往敵國和親媚污。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

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