阿里最新熱修復(fù)Sophix與QQ超級(jí)補(bǔ)丁和Tinker的實(shí)現(xiàn)與總結(jié)

2015年以來(lái)影斑,Android開(kāi)發(fā)領(lǐng)域里對(duì)熱修復(fù)技術(shù)的討論和分享越來(lái)越多,同時(shí)也出現(xiàn)了一些不同的解決方案俺叭,如QQ空間補(bǔ)丁方案铃拇、阿里AndFix以及微信Tinker(Bugly sdk也集成Tikner熱更新)和阿里最新出品Sophix.它們?cè)谠砀饔胁煌m用場(chǎng)景各異俩垃,到底采用哪種方案励幼,是開(kāi)發(fā)者比較頭疼的問(wèn)題。下面是這幾種技術(shù)方案介紹口柳。

技術(shù)背景

一苹粟、正常開(kāi)發(fā)流程

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

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

·用戶(hù)下載安裝成本太高

·

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

二六水、熱修復(fù)開(kāi)發(fā)流程

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

·無(wú)需重新發(fā)版辣卒,實(shí)時(shí)高效熱修復(fù)

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

·修復(fù)成功率高荣茫,把損失降到最低

業(yè)界熱門(mén)的熱修復(fù)技術(shù)

熱修復(fù)作為當(dāng)下熱門(mén)的技術(shù)想帅,在業(yè)界內(nèi)比較著名的有阿里巴巴的AndFix、Dexposed啡莉,騰訊QQ空間的超級(jí)補(bǔ)丁和微信的Tinker港准。最近阿里百川推出的HotFix熱修復(fù)服務(wù)就基于AndFix技術(shù),定位于線(xiàn)上緊急BUG的即時(shí)修復(fù)咧欣,所以AndFix技術(shù)這塊我們重點(diǎn)分析阿里百川HotFix浅缸。下面,我們就分別介紹QQ空間超級(jí)熱補(bǔ)丁技術(shù)和微信Tinker以及阿里百川的HotFix技術(shù)魄咕。

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

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

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

但是有一個(gè)問(wèn)題是做盅,當(dāng)兩個(gè)調(diào)用關(guān)系的類(lèi)不在同一個(gè)DEX時(shí)缤削,就會(huì)產(chǎn)生異常報(bào)錯(cuò)。我們知道吹榴,在APK安裝時(shí)僻他,虛擬機(jī)需要將classes.dex優(yōu)化成odex文件,然后才會(huì)執(zhí)行腊尚。在這個(gè)過(guò)程中,會(huì)進(jìn)行類(lèi)的verify操作满哪,如果調(diào)用關(guān)系的類(lèi)都在同一個(gè)DEX中的話(huà)就會(huì)被打上`CLASS_ISPREVERIFIED`的標(biāo)志婿斥,然后才會(huì)寫(xiě)入odex文件。

所以哨鸭,為了可以正常地進(jìn)行打補(bǔ)丁修復(fù)民宿,必須避免類(lèi)被打上`CLASS_ISPREVERIFIED`標(biāo)志,具體的做法就是單獨(dú)放一個(gè)類(lèi)在另外DEX中像鸡,讓其他類(lèi)調(diào)用活鹰。

修復(fù)的步驟為:

1.可以看出是通過(guò)獲取到當(dāng)前應(yīng)用的Classloader,即為BaseDexClassloader

2.通過(guò)反射獲取到他的DexPathList屬性對(duì)象pathList

3.通過(guò)反射調(diào)用pathList的dexElements方法把patch.dex轉(zhuǎn)化為Element[]

4.兩個(gè)Element[]進(jìn)行合并只估,把patch.dex放到最前面去

5.加載Element[]志群,達(dá)到修復(fù)目的

整體的流程圖如下:

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

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

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

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

不足:

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

2.為了實(shí)現(xiàn)修復(fù)這個(gè)過(guò)程兼贡,必須在應(yīng)用中加入兩個(gè)dex!dalvikhack.dex中只有一個(gè)類(lèi)攻冷,對(duì)性能影響不大,但是對(duì)于patch.dex來(lái)說(shuō)遍希,修復(fù)的類(lèi)到了一定數(shù)量等曼,就需要花不少的時(shí)間加載。對(duì)手淘這種航母級(jí)應(yīng)用來(lái)說(shuō)孵班,啟動(dòng)耗時(shí)增加2s以上是不能夠接受的事涉兽。

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

二冗懦、微信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í)占用額外磁盤(pán)空間凄贩,對(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ì)更高。

三捷雕、阿里百川HotFix

阿里百川推出的熱修復(fù)HotFix服務(wù)椒丧,相對(duì)于QQ空間超級(jí)補(bǔ)丁技術(shù)和微信Tinker來(lái)說(shuō),定位于緊急BUG修復(fù)的場(chǎng)景下救巷,能夠最及時(shí)的修復(fù)BUG壶熏,下拉補(bǔ)丁立即生效無(wú)需等待。

1浦译、AndFix實(shí)現(xiàn)原理

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

原理圖如下:

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

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

AndFix對(duì)ART設(shè)備支持,過(guò)程與Dalvik相似屡谐。

從技術(shù)原理鹰贵,不難看出阿里百川HotFix的幾個(gè)特點(diǎn):

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

1.

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

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

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

不足:

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

2.由于廠商的自定義ROM,對(duì)少數(shù)機(jī)型暫不支持枝哄。兼容性差肄梨。

(每一個(gè)java方法在art種都對(duì)應(yīng)一個(gè)ArtMethod, ArtMethod記錄了這個(gè)java方法的所有信息,包括所屬類(lèi)挠锥,訪問(wèn)權(quán)限众羡、代碼執(zhí)行地址。

通過(guò)evn->FromReflectedMethod,可以由Method對(duì)象得到這個(gè)方法對(duì)應(yīng)的ArtMethod的真正其實(shí)地址蓖租,然后就可以把它強(qiáng)轉(zhuǎn)為ArtMethod指針粱侣,從而對(duì)其所有的成員進(jìn)行修改。這樣就全部替換完之后就完成了熱修復(fù)邏輯蓖宦。以后調(diào)用這個(gè)方法時(shí)就會(huì)直接走到新方法的實(shí)現(xiàn)中了齐婴。

然而由于andfix里面的ArtMethod的結(jié)構(gòu)體遵照android虛擬機(jī)art源碼里面的ArtMethod構(gòu)建的,各個(gè)手機(jī)廠商對(duì)這個(gè)ArtMethod結(jié)構(gòu)體進(jìn)行修改就會(huì)導(dǎo)致喝原來(lái)開(kāi)源代碼里面的結(jié)構(gòu)不一致稠茂,那么在這個(gè)修改過(guò)的設(shè)備上柠偶,替換機(jī)制就會(huì)出問(wèn)題,無(wú)法正常執(zhí)行熱修復(fù)邏輯睬关。)

綜合分析如下:

對(duì)比總結(jié):

一诱担、qq空間超級(jí)補(bǔ)丁,微信Tinker類(lèi)似多DEX帶來(lái)的性能影響

我們知道电爹,多DEX方案原來(lái)是用于解決應(yīng)用方法數(shù)65k的問(wèn)題蔫仙,現(xiàn)在google也官方支持了MultiDex的實(shí)現(xiàn)方案。超級(jí)補(bǔ)丁技術(shù)和Tinker卻作為一種熱修復(fù)的方案藐不,平生給應(yīng)用增加了多個(gè)DEX匀哄,而多DEX技術(shù)最大的問(wèn)題在于性能上的坑秦效,因此基于這種方案的補(bǔ)丁技術(shù)影響應(yīng)用的性能是無(wú)疑的。

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

我們可以看到涎嚼,超級(jí)補(bǔ)丁技術(shù)和Tinker都選擇在Application的attachBaseContext()進(jìn)行補(bǔ)丁dex的加載阱州,即時(shí)這是加載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ì)進(jìn)行dexopt操作立哑,將patch.dex文件轉(zhuǎn)換成odex文件夜惭,這個(gè)過(guò)程本身非常耗時(shí)。而這個(gè)過(guò)程又要求在主線(xiàn)程中铛绰,以同步的方式執(zhí)行诈茧,否則無(wú)法成功進(jìn)行修復(fù)。就DEX的加載時(shí)間捂掰,大概做了以下的時(shí)間測(cè)試敢会。

通過(guò)上表可以看到,隨著patch.dex的尺寸增加这嚣,在不做任何優(yōu)化的情況下鸥昏,啟動(dòng)時(shí)間也直線(xiàn)增長(zhǎng)。對(duì)于一個(gè)應(yīng)用來(lái)說(shuō)姐帚,這簡(jiǎn)直是災(zāi)難性的吏垮。

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

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

除了應(yīng)用ANR以外,多DEX模式也同樣很容易導(dǎo)致Crash情況的出現(xiàn)友酱。在ART設(shè)備中為了保證不出現(xiàn)地址錯(cuò)亂晴音,需要把修改類(lèi)的所有相關(guān)類(lèi)全部加入到補(bǔ)丁中,這里會(huì)出現(xiàn)一個(gè)問(wèn)題缔杉,為了保證補(bǔ)丁包的體積最小锤躁,能否保證引入全部的關(guān)聯(lián)類(lèi)而不引入無(wú)關(guān)的類(lèi)呢?一旦沒(méi)有引入關(guān)聯(lián)的類(lèi),就會(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)類(lèi)而導(dǎo)致了更嚴(yán)重的Crash椒振,就更加的得不償失昭伸。

總的來(lái)說(shuō),熱修復(fù)本質(zhì)的目的是為了保證應(yīng)用更加穩(wěn)定澎迎,而不是為了更強(qiáng)大的功能引入更大的風(fēng)險(xiǎn)和不穩(wěn)定性庐杨。

二、熱修復(fù)or插件化?

我們經(jīng)常提到熱修復(fù)和插件化夹供,這都是當(dāng)下熱門(mén)的新興技術(shù)灵份。在講述之前,需要對(duì)這兩個(gè)概念進(jìn)行一下解釋哮洽。

·熱修復(fù):當(dāng)線(xiàn)上應(yīng)用出現(xiàn)緊急BUG填渠,為了避免重新發(fā)版,并且保證修復(fù)的及時(shí)性而進(jìn)行的一項(xiàng)在線(xiàn)推送補(bǔ)丁的修復(fù)方案鸟辅。

·插件化:一個(gè)程序劃分為不同的部分揭蜒,以插件的形式加載到應(yīng)用中去,本質(zhì)上它使用的技術(shù)還是熱修復(fù)技術(shù)剔桨,只是加入了更多工程實(shí)踐,讓它支持大規(guī)模的代碼更新以及資源和SO包的更新徙融。

顯然洒缀,從概念上我們可以看到,插件化使用場(chǎng)景更多是功能上的欺冀,熱修復(fù)強(qiáng)調(diào)微小的修復(fù)树绩。從這個(gè)層面來(lái)說(shuō),插件化必然功能更加強(qiáng)大隐轩,能做的事情也更多饺饭。QQ空間超級(jí)補(bǔ)丁技術(shù)和微信Tinker從類(lèi)、資源的替換和更新上來(lái)看职车,與其說(shuō)是熱修復(fù)瘫俊,不如說(shuō)是插件化技術(shù)的實(shí)踐。

QQ空間超級(jí)補(bǔ)丁技術(shù)和微信Tinker提供了更加強(qiáng)大的功能悴灵,但是對(duì)應(yīng)用的性能和穩(wěn)定有較大的影響扛芽,就BUG修復(fù)的這個(gè)使用場(chǎng)景上還不夠明確,并且顯得過(guò)重积瞒。在插件化開(kāi)發(fā)上川尖,有用武之地。

同樣andfix兼容又有很大的問(wèn)題茫孔。

終于進(jìn)入主題阿里最新推出的熱修復(fù)方案技術(shù)sophfix.

Sophix設(shè)計(jì)理念:

Sophix的核心設(shè)計(jì)理念叮喳,就是非侵入性被芳。

我們的打包過(guò)程不會(huì)侵入到apk的build流程中。我們所需要的馍悟,只有已經(jīng)生成完畢的新舊apk畔濒,而至于apk是如何生成的——是Android

Studio打包出來(lái)的、還是Eclipse打包出來(lái)的赋朦、或者是自定義的打包流程篓冲,我們一律不關(guān)心。在生成補(bǔ)丁的過(guò)程中間既不會(huì)改變?nèi)魏未虬M件宠哄,也不插入任何AOP代碼壹将,我們極力做到了——不添加任何超出開(kāi)發(fā)者預(yù)期的代碼,以避免多余的熱修復(fù)代碼給開(kāi)發(fā)者帶來(lái)困擾毛嫉。

在Sophix中诽俯,唯一需要的就是初始化和請(qǐng)求補(bǔ)丁兩行代碼,甚至連入口Application類(lèi)我們都不做任何修改承粤,這樣就給了開(kāi)發(fā)者最大的透明度和自由度暴区。我們甚至重新開(kāi)發(fā)了打包工具,使得補(bǔ)丁工具操作圖形界面化辛臊,這種所見(jiàn)即所得的補(bǔ)丁生成方式也是阿里熱修復(fù)獨(dú)家的仙粱。因此,Sophix的接入成本也是目前市面上所有方案里最低的彻舰。

這種非侵入式熱更新理念伐割,是我們?cè)谠O(shè)計(jì)過(guò)程中從用戶(hù)使用角度進(jìn)行了深入思考而提煉出的核心思想。

這里的用戶(hù)刃唤,指的自然是廣大的開(kāi)發(fā)者隔心。對(duì)于開(kāi)發(fā)者而言,熱修復(fù)應(yīng)該是一個(gè)與業(yè)務(wù)無(wú)關(guān)的SDK組件尚胞,在整個(gè)開(kāi)發(fā)過(guò)程中感知不到它的存在硬霍。最理想的情況,就是開(kāi)發(fā)者拿過(guò)來(lái)兩個(gè)apk笼裳,一個(gè)是已經(jīng)安裝在手機(jī)上的apk唯卖,另一個(gè)是將要發(fā)布出去的apk。我們直接通過(guò)工具躬柬,就可以根據(jù)這兩個(gè)apk生成補(bǔ)丁耐床,然后把這個(gè)補(bǔ)丁下發(fā)給已經(jīng)安裝的舊app上,就可以直接加載楔脯,使舊app重生為新的app撩轰。而這個(gè)加載了補(bǔ)丁包新app,在功能和使用上,將會(huì)和直接安裝新apk別無(wú)二致堪嫂。

Sophfix與其他熱修復(fù)方案對(duì)比:

可以看到偎箫,Sophix在各個(gè)指標(biāo)上全面占優(yōu)。而其中唯一不支持的地方就是四大組件的修復(fù)皆串,這是因?yàn)槿绻迯?fù)四大組件淹办,必須在AndroidManifest里面預(yù)先插入代理組件,并且盡可能聲明所有權(quán)限恶复,而這么做就會(huì)給原先的app添加很多臃腫的代碼怜森,對(duì)app運(yùn)行流程的侵入性很強(qiáng)。

Sophix支持代碼修復(fù)谤牡、資源修復(fù)副硅、so庫(kù)修復(fù)。下面對(duì)這三種修復(fù)進(jìn)行介紹翅萤。

恐疲、代碼修復(fù)

代碼修復(fù)有兩大主要方案,一種是阿里系的底層替換方案套么,另一種是騰訊系的類(lèi)加載方案培己。

這兩類(lèi)方案各有優(yōu)劣:

底層替換方案限制頗多,但時(shí)效性最好胚泌,加載輕快省咨,立即見(jiàn)效。

類(lèi)加載方案時(shí)效性差玷室,需要重新冷啟動(dòng)才能見(jiàn)效零蓉,但修復(fù)范圍廣,限制少阵苇。

底層替換方案

底層替換方案是在已經(jīng)加載了的類(lèi)中直接替換掉原有方法,是在原來(lái)類(lèi)的基礎(chǔ)上進(jìn)行修改的感论。因而無(wú)法實(shí)現(xiàn)對(duì)與原有類(lèi)進(jìn)行方法和字段的增減绅项,因?yàn)檫@樣將破壞原有類(lèi)的結(jié)構(gòu)。

一旦補(bǔ)丁類(lèi)中出現(xiàn)了方法的增加和減少比肄,就會(huì)導(dǎo)致這個(gè)類(lèi)以及整個(gè)Dex的方法數(shù)的變化快耿。方法數(shù)的變化伴隨著方法索引的變化,這樣在訪問(wèn)方法時(shí)就無(wú)法正常地索引到正確的方法了芳绩。

如果字段發(fā)生了增加和減少掀亥,和方法變化的情況一樣,所有字段的索引都會(huì)發(fā)生變化妥色。并且更嚴(yán)重的問(wèn)題是搪花,如果在程序運(yùn)行中間某個(gè)類(lèi)突然增加了一個(gè)字段,那么對(duì)于原先已經(jīng)產(chǎn)生的這個(gè)類(lèi)的實(shí)例,它們還是原來(lái)的結(jié)構(gòu)撮竿,這是無(wú)法改變的吮便。而新方法使用到這些老的實(shí)例對(duì)象時(shí),訪問(wèn)新增字段就會(huì)產(chǎn)生不可預(yù)期的結(jié)果幢踏。

這是這類(lèi)方案的固有限制髓需,而底層替換方案最為人詬病的地方,在于底層替換的不穩(wěn)定性房蝉。

傳統(tǒng)的底層替換方式僚匆,不論是Dexposed、Andfix或者其他安全界的Hook方案搭幻,都是直接依賴(lài)修改虛擬機(jī)方法實(shí)體的具體字段咧擂。例如,改Dalvik方法的jni函數(shù)指針粗卜、改類(lèi)或方法的訪問(wèn)權(quán)限等等屋确。這樣就帶來(lái)一個(gè)很?chē)?yán)重的問(wèn)題,由于Android是開(kāi)源的续扔,各個(gè)手機(jī)廠商都可以對(duì)代碼進(jìn)行改造攻臀,而Andfix里ArtMethod的結(jié)構(gòu)是根據(jù)公開(kāi)的Android源碼中的結(jié)構(gòu)寫(xiě)死的。如果某個(gè)廠商對(duì)這個(gè)ArtMethod結(jié)構(gòu)體進(jìn)行了修改纱昧,就和原先開(kāi)源代碼里的結(jié)構(gòu)不一致刨啸,那么在這個(gè)修改過(guò)了的設(shè)備上,通用性的替換機(jī)制就會(huì)出問(wèn)題识脆。這便是不穩(wěn)定的根源设联。

而我們也對(duì)代碼的底層替換原理重新進(jìn)行了深入思考,從克服其限制和兼容性入手灼捂,以一種更加優(yōu)雅的替換思路离例,實(shí)現(xiàn)了即時(shí)生效的代碼熱修復(fù)。sophix實(shí)現(xiàn)的是一種無(wú)視底層具體結(jié)構(gòu)的替換方式悉稠,也就是把原先這樣的逐一替換:

變成了這樣的整體替換:

這么一來(lái)宫蛆,我們不僅解決了兼容性問(wèn)題,并且由于忽略了底層ArtMethod結(jié)構(gòu)的差異的猛,對(duì)于所有的Android版本都不再需要區(qū)分耀盗,代碼量大大減少。即使以后的Android版本不斷修改ArtMethod的成員卦尊,只要保證ArtMethod數(shù)組仍是以線(xiàn)性結(jié)構(gòu)排列叛拷,就能直接適用于將來(lái)的Android 8.0、9.0等新版本岂却,無(wú)需再針對(duì)新的系統(tǒng)版本進(jìn)行適配了忿薇。

事實(shí)也證明確實(shí)如此裙椭,當(dāng)我們拿到Google剛發(fā)不久的Android O(8.0)開(kāi)發(fā)者預(yù)覽版的系統(tǒng)時(shí),hotfix demo直接就能順利地加載補(bǔ)丁跑起來(lái)了煌恢,我們并沒(méi)有做任何適配工作骇陈,穩(wěn)定性極好。

類(lèi)加載方案

類(lèi)加載方案的原理是在app重新啟動(dòng)后讓Classloader去加載新的類(lèi)瑰抵。因?yàn)樵赼pp運(yùn)行到一半的時(shí)候你雌,所有需要發(fā)生變更的類(lèi)已經(jīng)被加載過(guò)了,在Android上是無(wú)法對(duì)一個(gè)類(lèi)進(jìn)行卸載的二汛。如果不重啟婿崭,原來(lái)的類(lèi)還在虛擬機(jī)中,就無(wú)法加載新類(lèi)肴颊。因此氓栈,只有在下次重啟的時(shí)候,在還沒(méi)走到業(yè)務(wù)邏輯之前搶先加載補(bǔ)丁中的新類(lèi)婿着,這樣后續(xù)訪問(wèn)這個(gè)類(lèi)時(shí)授瘦,就會(huì)Resolve為新類(lèi)。從而達(dá)到熱修復(fù)的目的竟宋。

再來(lái)看看騰訊系三大類(lèi)加載方案的實(shí)現(xiàn)原理提完。QQ空間方案會(huì)侵入打包流程,并且為了hack添加一些無(wú)用的信息丘侠,實(shí)現(xiàn)起來(lái)很不優(yōu)雅徒欣。而QFix的方案,需要獲取底層虛擬機(jī)的函數(shù)蜗字,不夠穩(wěn)定可靠打肝,并且有個(gè)比較大的問(wèn)題是無(wú)法新增public函數(shù)。

微信的Tinker方案是完整的全量dex加載挪捕,并且可謂是將補(bǔ)丁合成做到了極致粗梭,然而我們發(fā)現(xiàn),精密的武器并非適用于所有戰(zhàn)場(chǎng)级零。Tinker的合成方案断医,是從dex的方法和指令維度進(jìn)行全量合成躲叼,整個(gè)過(guò)程都是自己研發(fā)的赁严。

雖然可以很大地節(jié)省空間,但由于對(duì)dex內(nèi)容的比較粒度過(guò)細(xì),實(shí)現(xiàn)較為復(fù)雜亥贸,性能消耗比較嚴(yán)重。實(shí)際上浇垦,dex的大小占整個(gè)apk的比例是比較低的炕置,一個(gè)app里面的dex文件大小并不是主要部分,而占空間大的主要還是資源文件。因此朴摊,Tinker方案的時(shí)空代價(jià)轉(zhuǎn)換的性?xún)r(jià)比不高默垄。

其實(shí),dex比較的最佳粒度甚纲,應(yīng)該是在類(lèi)的維度口锭。它既不像方法和指令維度那樣的細(xì)微,也不像bsbiff比較那般的粗糙介杆。在類(lèi)的維度鹃操,可以達(dá)到時(shí)間和空間平衡的最佳效果〈荷冢基于這個(gè)準(zhǔn)則荆隘,我們另辟蹊徑,實(shí)現(xiàn)了一種完全不同的全量dex替換方案赴背。

sophix采用的也是全量合成dex的技術(shù)椰拒,這個(gè)技術(shù)是從手淘插件化框架Atlas汲取的。直接利用Android原先的類(lèi)查找和合成機(jī)制凰荚,快速合成新的全量dex燃观。這么一來(lái),我們既不需要處理合成時(shí)方法數(shù)超過(guò)的情況浇揩,對(duì)于dex的結(jié)構(gòu)也不用進(jìn)行破壞性重構(gòu)仪壮。

從圖中可以看到,我們重新編排了包中dex的順序胳徽。這樣积锅,在虛擬機(jī)查找類(lèi)的時(shí)候,會(huì)優(yōu)先找到classes.dex中的類(lèi)养盗,然后才是classes2.dex缚陷、classes3.dex,也可以看做是dex文件級(jí)別的類(lèi)插樁方案往核。這個(gè)方式十分巧妙箫爷,它對(duì)舊包與補(bǔ)丁包中classes.dex的順序進(jìn)行了打破與重組,最終使得系統(tǒng)可以自然地識(shí)別到這個(gè)順序聂儒,以實(shí)現(xiàn)類(lèi)覆蓋的目的虎锚。這將會(huì)大大減少合成補(bǔ)丁的開(kāi)銷(xiāo)。

雙劍合璧

既然底層替換方案和類(lèi)加載方案各有其優(yōu)點(diǎn)衩婚,把他們聯(lián)合起來(lái)不是最好的選擇嗎?Sophix的代碼修復(fù)體系正是同時(shí)涵蓋了這兩種方案窜护。兩種方案的結(jié)合,可以實(shí)現(xiàn)優(yōu)勢(shì)互補(bǔ)非春,完全兼顧的作用柱徙,可以靈活地根據(jù)實(shí)際情況自動(dòng)切換缓屠。

這兩種方案我們都進(jìn)行了重大的改進(jìn),并且從補(bǔ)丁生成到應(yīng)用的各個(gè)環(huán)節(jié)都進(jìn)行了研究护侮,使得二者能很好地整合在一起敌完。在補(bǔ)丁生成階段,補(bǔ)丁工具會(huì)根據(jù)實(shí)際代碼變動(dòng)情況進(jìn)行自動(dòng)選擇羊初,針對(duì)小修改滨溉,在底層替換方案限制范圍內(nèi)的,就直接采用底層替換修復(fù)嗎长赞,這樣可以做到代碼修復(fù)即時(shí)生效业踏。而對(duì)于代碼修改超出底層替換限制的,會(huì)使用類(lèi)加載替換涧卵,這樣雖然及時(shí)性沒(méi)那么好勤家,但總歸可以達(dá)到熱修復(fù)的目的。

另外柳恐,運(yùn)行時(shí)階段伐脖,Sophix還會(huì)再判斷所運(yùn)行的機(jī)型是否支持熱修復(fù),這樣即使補(bǔ)丁支持熱修復(fù)乐设,但由于機(jī)型底層虛擬機(jī)構(gòu)造不支持讼庇,還是會(huì)走類(lèi)加載修復(fù),從而達(dá)到最好的兼容性近尚。最后也要注意

蠕啄、資源修復(fù)

不android資源熱修復(fù),就是在app不重新安裝的情況下戈锻,利用下發(fā)補(bǔ)丁包直接更新本app中的資源歼跟。

目前市面上的資源熱修復(fù)方案基本上都是參考Instant Run的實(shí)現(xiàn)。Instant Run實(shí)現(xiàn)過(guò)程大概分為兩部:

1格遭、構(gòu)造一個(gè)新的AssetManager,并通過(guò)反射條用addAssetPath,把這個(gè)完整的新資源包加入到AssetManager中哈街。這樣就得到了一個(gè)含有所有新資源的AssetManager。

2拒迅、找到所有之前引用到原油AssetManager的地方骚秦,通過(guò)反射,把引用處替換為AssetManager

這種方式下發(fā)完整的包很占用空間璧微。而像有些方案作箍,是先進(jìn)行對(duì)資源包做差量,在運(yùn)行時(shí)合成完整包再加載前硫。這樣確實(shí)減少包的體積胞得,但是在運(yùn)行時(shí)多了合成的操作,耗費(fèi)了運(yùn)行時(shí)間喝內(nèi)存开瞭。合成后的包也是完整的包懒震,仍舊會(huì)占磁盤(pán)空間。

Sophix采用的一種很巧妙的方式嗤详,構(gòu)造一個(gè)package id為0x66的資源包个扰,這個(gè)包里面只包含改變了的資源項(xiàng),然后直接在原來(lái)的AssetManager中addAssetPaht這個(gè)包葱色。然后递宅,就可以了。憂(yōu)郁補(bǔ)丁包的package id為0x66和原來(lái)文件的package id為0x7f不沖突苍狰,因此直接加入到一有的AssetManager中就直接使用了办龄。補(bǔ)丁包里面的資源,只包含原油包里面沒(méi)有而新包里有的資源以及內(nèi)容發(fā)生改變的資源淋昭。

資源的改變包含增加俐填、減少、修改翔忽,分別處理方法如下:

1英融、新增資源,直接加入布丁包

2歇式、減少的資源驶悟,我們只要不使用就行了,因此不用考慮這種情況材失,它不影響布丁包

3痕鳍、對(duì)于修改資源,比如替換了一張圖片之類(lèi)的情況龙巨,我們把他視為新增資源笼呆,在打入補(bǔ)丁的時(shí)候,代碼在引用出也會(huì)做相應(yīng)的修改旨别,也就是直接把原來(lái)使用的舊資源id的地方變?yōu)樾耰d抄邀。

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

1.不修改AssetManager的引用處,替換更快更完全昼榛。(對(duì)比Instanat Run以及所有copycat的實(shí)現(xiàn))

2.不必下發(fā)完整包境肾,補(bǔ)丁包中只包含有變動(dòng)的資源。(對(duì)比Instanat Run胆屿、Amigo等方式的實(shí)現(xiàn))

3.不需要在運(yùn)行時(shí)合成完整包奥喻。不占用運(yùn)行時(shí)計(jì)算和內(nèi)存資源。(對(duì)比Tinker的實(shí)現(xiàn))

三非迹、so庫(kù)修復(fù)

so庫(kù)的修復(fù)本質(zhì)上是對(duì)native方法的修復(fù)和替換环鲤。

我們知道JNI編程中,native方法可以通過(guò)動(dòng)態(tài)注冊(cè)和靜態(tài)注冊(cè)兩種方式進(jìn)行憎兽。動(dòng)態(tài)注冊(cè)的native方法必須實(shí)現(xiàn)`JNI_OnLoad`方法冷离,同時(shí)實(shí)現(xiàn)一個(gè)`JNINativeMethod[]`數(shù)組吵冒,靜態(tài)注冊(cè)的native方法必須是`Java+類(lèi)完整路徑+方法名`的格式。

動(dòng)態(tài)注冊(cè)的native方法映射通過(guò)加載so庫(kù)過(guò)程中調(diào)用JNI_OnLoad方法調(diào)用完成西剥,靜態(tài)注冊(cè)的native方法映射是在該native方法第一次執(zhí)行的時(shí)候才完成映射痹栖,當(dāng)然前提是該so庫(kù)已經(jīng)load過(guò)。

我們采用的是類(lèi)似類(lèi)修復(fù)反射注入方式瞭空。把補(bǔ)丁so庫(kù)的路徑插入到nativeLibraryDirectories數(shù)組的最前面揪阿,就能夠達(dá)到加載so庫(kù)的時(shí)候是補(bǔ)丁so庫(kù),而不是原來(lái)so庫(kù)的目錄咆畏,從而達(dá)到修復(fù)的目的南捂。

采用這種方案,完全由Sophix在啟動(dòng)期間反射注入patch中的so庫(kù)旧找。對(duì)開(kāi)發(fā)者依然是透明的溺健。不用像某些其他方案需要手動(dòng)替換系統(tǒng)的System.load來(lái)實(shí)現(xiàn)替換目的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钮蛛,一起剝皮案震驚了整個(gè)濱河市矿瘦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌愿卒,老刑警劉巖缚去,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異琼开,居然都是意外死亡易结,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)柜候,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)搞动,“玉大人,你說(shuō)我怎么就攤上這事渣刷○兄祝” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵辅柴,是天一觀的道長(zhǎng)箩溃。 經(jīng)常有香客問(wèn)我,道長(zhǎng)碌嘀,這世上最難降的妖魔是什么涣旨? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮股冗,結(jié)果婚禮上霹陡,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好烹棉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布攒霹。 她就那樣靜靜地躺著,像睡著了一般浆洗。 火紅的嫁衣襯著肌膚如雪催束。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天辅髓,我揣著相機(jī)與錄音,去河邊找鬼少梁。 笑死洛口,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凯沪。 我是一名探鬼主播第焰,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼妨马!你這毒婦竟也來(lái)了挺举?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤烘跺,失蹤者是張志新(化名)和其女友劉穎湘纵,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體滤淳,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡梧喷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脖咐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铺敌。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖屁擅,靈堂內(nèi)的尸體忽然破棺而出偿凭,到底是詐尸還是另有隱情,我是刑警寧澤派歌,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布弯囊,位于F島的核電站,受9級(jí)特大地震影響胶果,放射性物質(zhì)發(fā)生泄漏常挚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一稽物、第九天 我趴在偏房一處隱蔽的房頂上張望奄毡。 院中可真熱鬧,春花似錦贝或、人聲如沸吼过。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)盗忱。三九已至酱床,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間趟佃,已是汗流浹背扇谣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闲昭,地道東北人罐寨。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像序矩,于是被迫代替她去往敵國(guó)和親鸯绿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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