DexDiff:基于dex文件反編譯生成dex增量包

前段時(shí)間微信分享了一篇文章——微信Android熱補(bǔ)丁實(shí)踐演進(jìn)之路, 這篇文章主要講了目前流行的Android熱修復(fù)方案,同時(shí)微信在QZone方案的基礎(chǔ)上優(yōu)化出一套dex全量替換的熱修復(fù)方案(Tinker)就轧。個(gè)人認(rèn)為微信的這套方案盡管規(guī)避掉了Qzone方案中插樁導(dǎo)致的問題陷寝,但是由于需要在運(yùn)行時(shí)加載全量的dex仰坦,這可能會在運(yùn)行時(shí)內(nèi)存占用上有一定的影響扣孟,目前這個(gè)僅僅是猜測官帘,有待調(diào)研瞬雹。

GitHub上已有Tinker方案仿版——Tinker_imitator,但帶有個(gè)人感情色彩且不負(fù)責(zé)任地說刽虹,這套方案基本是堆砌各項(xiàng)技術(shù)然后封裝成套裝酗捌,應(yīng)該屬于比較糙的仿版,方案在生成差分dex的時(shí)候采用的是現(xiàn)成的bsdiff算法涌哲,這個(gè)通用的二進(jìn)制差分算法很牛逼胖缤,但由于不能很好的利用dex本身的結(jié)構(gòu),因此會導(dǎo)致可能僅僅添加一句代碼也需要下發(fā)不小的增量包(參考chrome增量更新小胡瓜中的見解)阀圾,相對于微信文章中提到的自研DexDiff算法來說哪廓,這個(gè)就相對弱一點(diǎn)了。

這么一大段背景介紹初烘,我想表達(dá)的是我認(rèn)為微信這篇文章對我而言最關(guān)鍵的價(jià)值在于DexDiff的概念涡真,看完這篇文章之后,我去學(xué)習(xí)了bsdiff算法肾筐,學(xué)習(xí)完發(fā)現(xiàn)bsdiff算法用于apk的增量更新或許不錯(cuò)哆料,但是用來做dex的增量包的確是少了許多對dex進(jìn)行量身定制的優(yōu)勢,于是我嘗試去思考如何根據(jù)dex的數(shù)據(jù)格式來實(shí)現(xiàn)DexDiff吗铐,本文主要分享我的一些思路东亦,希望起到拋磚引玉的作用。

dex文件格式

dex文件是運(yùn)行在Dalvik中的字節(jié)碼文件唬渗,類似于運(yùn)行于JVM中的class文件典阵,熟悉class文件格式的同學(xué)應(yīng)該很容易理解,dex文件的布局可以用下圖來進(jìn)行說明:

dex文件布局

如果想更細(xì)致地了解dex文件中每部分?jǐn)?shù)據(jù)的具體格式以及意義镊逝,請移步至官方文檔Dalvik Executable format壮啊,建議可以使用010 Editor學(xué)習(xí)dex文件,當(dāng)打開dex文件時(shí)該編輯器會自動推薦安裝解析dex文件的插件撑蒜,安裝完插件便能與dex文件愉快地玩耍了他巨。

反編譯dex文件

了解了dex文件的基本格式之后充坑,就可以開始dex文件的反編譯之旅了减江,Android反編譯套裝(apktool染突、smail、dex2jar辈灼、jd-gui)現(xiàn)在基本是居家必備了份企,而今天要出場的大牌就是dex2jar(~~Orz),dex2jar采用了跟asm一樣的套路巡莹,搖身一變便成了解析和生成dex文件的神器司志,建議沒看過asm源碼的Android開發(fā)小伙伴,可以直接擼一把dex2jar的源碼降宅,相信會有不少收獲骂远,至少對了解dex文件的內(nèi)部數(shù)據(jù)格式來講,會得到量與質(zhì)的提升腰根。源碼都亮出來了激才,似乎沒必要在解釋下去了,但是為了能夠圓潤地過渡额嘿,我決定還是說明下dex2jar解析完dex文件后的數(shù)據(jù)結(jié)構(gòu):

dex2jar解析完dex文件后的數(shù)據(jù)結(jié)構(gòu)

dex2jar實(shí)際上是依據(jù)Java中Class的屬性來設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)的瘸恼,自上而下,一目了然册养。同時(shí)东帅,數(shù)據(jù)結(jié)構(gòu)中的各項(xiàng)數(shù)據(jù)都能在dex文件找到對應(yīng)的數(shù)據(jù)塊:比如className對應(yīng)于dex文件格式中的type_id_item,而type_id_item指向的是string_id_item球拦,通過string_id_item中的偏移便可以定位到data section中相應(yīng)的string_data_item靠闭,從而解析出類字符串;再比如annotations對應(yīng)于dex中的annotation_set_item坎炼,而annotation_set_item包含多個(gè)annotation_item愧膀,每一個(gè)annotation_item都可以通過其encoded_annotation解析出對應(yīng)的type_id_item以及包含的key-value對,從而可以得到修飾類点弯、字段或方法的注解數(shù)據(jù)扇调。如果想要更全面更細(xì)致地了解如何解析dex文件得到上述數(shù)據(jù)結(jié)構(gòu)集,請擼dex2jar抢肛。

dex文件的差分與合成

前奏終于結(jié)束了狼钮,可以開始正題了,不過不要擔(dān)心捡絮,因?yàn)檎私?jīng)的內(nèi)容會比你想象的少很多熬芜。DexDiff的目的在于對比新舊dex生成補(bǔ)丁數(shù)據(jù),然后利用補(bǔ)丁數(shù)據(jù)和舊dex合成新dex福稳,目前DexDiff的實(shí)現(xiàn)中涎拉,我的思路是這樣的:

  1. 基于dex2jar庫反編譯新舊dex
  2. 逐個(gè)對比新舊dex中的class:
    • 若class僅存在于舊dex,保存舊dex中的class至deleteClasses
    • 若class僅存在于新dex,保存新dex中的class至replaceClasses
    • 若class存在于新舊dex鼓拧,但class數(shù)據(jù)不一致半火,保存新dex中的class至replaceClasses
  3. 編譯得到的replaceClasses得到replace.dex
  4. 記錄deleteClasses中類的標(biāo)識字符串得到delete.data
  5. 根據(jù)replace.dex、delete.data以及舊dex便可以使用dex2jar編譯得到新dex

其中對比新舊dex中兩個(gè)對應(yīng)的class以確定其是否一致時(shí)季俩,采用的方式是逐個(gè)對比class中的屬性(accessFlags钮糖,superClass,interfaces酌住,fields店归,methods等),若有一項(xiàng)屬性不一致則定義該class為需要用新dex中數(shù)據(jù)替換舊dex中對應(yīng)數(shù)據(jù)酪我。通過這樣的方式可以實(shí)現(xiàn)class粒度上的dex差分消痛,這樣程度的差分可以應(yīng)用于生成QZone方案中的熱修復(fù)補(bǔ)丁包了,如果做增量更新的話都哭,class粒度下的增量包大小也是可以接受的秩伞。實(shí)現(xiàn)DexDiff的思路就是這么簡單粗暴,但是在實(shí)現(xiàn)的過程中還是也還是一波三折质涛,尤其是在比較method中的指令部分時(shí)遇到了不少坑稠歉,項(xiàng)目源碼已經(jīng)上傳至GitHub DexDiff

優(yōu)化思路

目前版本中做差分的粒度是class汇陆,但是實(shí)際上同樣的思路可以實(shí)現(xiàn)method粒度以及instruction粒度的差分怒炸。方法級別的差分做法應(yīng)該可以完全照搬class級別的差分做法;而instruction粒度的差分需要做適當(dāng)?shù)恼{(diào)整和改進(jìn)毡代,因?yàn)閕nstruction級別不能像class或method一樣可以直接整個(gè)替換阅羹,我們需要對比指令的語義,并且記錄指令的刪除或插入位置偏移教寂,初步的想法是可以設(shè)計(jì)自己的數(shù)據(jù)格式來存儲這些指令差異捏鱼,但這在我估計(jì)總體的實(shí)現(xiàn)難度跟class粒度方案比應(yīng)該不在一個(gè)量級,并且應(yīng)用instruction粒度方案時(shí)的整體的穩(wěn)定性酪耕、兼容性等都將是較大的挑戰(zhàn)导梆,單從技術(shù)上來講還是值得試的,如果后續(xù)完成實(shí)踐再分享迂烁。

GitHub

DexDiff:求圍觀_

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末看尼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子盟步,更是在濱河造成了極大的恐慌藏斩,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件却盘,死亡現(xiàn)場離奇詭異狰域,居然都是意外死亡媳拴,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門兆览,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屈溉,“玉大人,你說我怎么就攤上這事拓颓∮镉ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵驶睦,是天一觀的道長。 經(jīng)常有香客問我匿醒,道長场航,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任廉羔,我火速辦了婚禮溉痢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘憋他。我一直安慰自己孩饼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布竹挡。 她就那樣靜靜地躺著镀娶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪揪罕。 梳的紋絲不亂的頭發(fā)上梯码,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機(jī)與錄音好啰,去河邊找鬼轩娶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛框往,可吹牛的內(nèi)容都是我干的鳄抒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼椰弊,長吁一口氣:“原來是場噩夢啊……” “哼许溅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起男应,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤闹司,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后沐飘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體游桩,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡牲迫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了借卧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盹憎。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖铐刘,靈堂內(nèi)的尸體忽然破棺而出陪每,到底是詐尸還是另有隱情,我是刑警寧澤镰吵,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布檩禾,位于F島的核電站,受9級特大地震影響疤祭,放射性物質(zhì)發(fā)生泄漏盼产。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一勺馆、第九天 我趴在偏房一處隱蔽的房頂上張望戏售。 院中可真熱鬧,春花似錦草穆、人聲如沸灌灾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锋喜。三九已至,卻和暖如春诗祸,著一層夾襖步出監(jiān)牢的瞬間跑芳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工直颅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留博个,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓功偿,卻偏偏與公主長得像盆佣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子械荷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

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