哥哥手把手教你Android熱更新,到底是更新啥?

正常開發(fā)流程:

新版本上線,發(fā)現(xiàn)問題或用戶反饋bug,緊急修復(fù),上線版本,用戶重新安裝。

熱修復(fù)流程:

新版本上線,發(fā)現(xiàn)問題或用戶反饋,緊急修復(fù),上線補丁,自動修復(fù)

Thinker解決思路?

在android5.0之前,每個android應(yīng)用只含有一個dex文件,dex的方法數(shù)量被限制在了65535之內(nèi),導(dǎo)致apk引入大量第三方sdk后方法數(shù)量超過限制無法編譯通過。為了解決這個問題,Google推出多dex文件的解決方案multidex,一個apk可以包含多個dex文件。通過Multidex.install(this)完成dex文件的加載。

Tinker方案參考multidex實現(xiàn)原理,在編譯時通過新舊兩個Dex生成差異patch.dex。在運行時,將差異patch.dex重新和原始安裝包的舊Dex合并還原為新的Dex。這個過程可能比較耗費時間與內(nèi)存,所以tinker單獨放在一個后臺進程:patch中處理。為了補丁包盡量的小,微信自研了DexDiff算法,它深度利用Dex的格式來減少差異的大小。由于采用ClassLoader機制,所以需要app重啟。

Tinker基于基版本利用自研的DexDiff算法生成差分包,放在后臺。App啟動時下載差分包,與原dex重新合并,解壓縮,并參考MultiDex.install()流程,重新安裝app。

由于類加載實現(xiàn)原理涉及dex文件的重新解壓縮合并等處理,消耗內(nèi)存大,耗時長,在系統(tǒng)低內(nèi)存時容易導(dǎo)致熱更新失敗,騰訊測試成功率大概為95%。實際熱部署時,差分包應(yīng)文件大小最小。

綜上沒有完美的熱更新方案,沒有100%的熱更新成功率。目前,騰訊tinker基本可以滿足app的熱更新需求,但隨著app用戶規(guī)模不斷增大,業(yè)務(wù)需求日益復(fù)雜,可考慮阿里的sophix商業(yè)方案,sophix同時應(yīng)用類加載和底層替換兩種方案,具有底層替換的修改及時性,和類加載方案的兼容性等優(yōu)點。而且sophix采用非侵入打包,圖形化的生成補丁,用阿里的原話說就是“傻瓜式接入”,點一點鼠標就能生成補丁文件,而且阿里提供了后臺補丁管理系統(tǒng),幫助開發(fā)者在生成補丁后直接上傳至阿里的后臺,無需開發(fā)者在自己的app和服務(wù)端做任何的操作。

熱部署” – 方法內(nèi)的簡單修改,無需重啟app和Activity。 “暖部署” – app無需重啟,但是activity需要重啟,比如資源的修改。 “冷部署” – app需要重啟,比如繼承關(guān)系的改變或方法的簽名變化等。?

熱更新究竟是什么?

有一些這樣的情況, 當一個App發(fā)布之后,突然發(fā)現(xiàn)了一個嚴重bug需要進行緊急修復(fù),這時候公司各方就會忙得焦頭爛額:重新打包App、測試、向各個應(yīng)用市場和渠道換包、提示用戶升級、用戶下載、覆蓋安裝。有時候僅僅是為了修改了一行代碼,也要付出巨大的成本進行換包和重新發(fā)布。

這種需要替換運行時新的類和資源文件的加載,就可以認為是熱操作了。而在熱更新出現(xiàn)之前,通過反射注解、反射調(diào)用和反射注入等方式已經(jīng)可以實現(xiàn)類的動態(tài)加載了。而熱更新框架的出現(xiàn)就是為了解決這樣一個問題的。從某種意義上來說,熱更新就是要做一件事,替換。當替換的東西屬于大塊內(nèi)容的時候,就是模塊化了,當你去替換方法的時候,叫熱更新,當你替換類的時候,加熱插件,而且重某種意義上講,所有的熱更新方案,都是一種熱插件,因為熱更新方案就是在app之外去干這個事。就這么簡單的理解。無論是替換一個類,還是一個方法,都是在干替換這件事請。。這里的替換,也算是幾種hook操作,無論在什么代碼等級上,都是一種侵入性的操作。

所以總結(jié)一句話簡單理解熱更新 HotFix 就是改變app運行行為的技術(shù)!(或者說就是對已發(fā)布app進行bug修復(fù)的技術(shù))

目前存在的主流框架?

先后出現(xiàn)了Dexposed、AndFix,Qzone超級補丁的類Nuwa方式,微信的Tinker, 大眾點評的nuwa、百度金融的rocooFix, 餓了么的amigo以及美團的robust.以及阿里的Sophix.騰訊的內(nèi)部方案KKFix.

框架橫向?qū)Ρ龋?/h1>

實現(xiàn)套路?

對比點評?

AndFix作為native解決方案,首先面臨的是穩(wěn)定性與兼容性問題,更重要的是它無法實現(xiàn)類替換,它是需要大量額外的開發(fā)成本的;

Robust兼容性與成功率較高,但是它與AndFix一樣,無法新增變量與類只能用做的bugFix方案;

Qzone方案可以做到發(fā)布產(chǎn)品功能,但是它主要問題是插樁帶來Dalvik的性能問題,以及為了解決Art下內(nèi)存地址問題而導(dǎo)致補丁包急速增大的。

特別是在Android N之后,由于混合編譯的inline策略修改,對于市面上的各種方案都不太容易解決。而Tinker熱補丁方案不僅支持類、So以及資源的替換,它還是2.X-8.X(1.9.0以上支持8.X)的全平臺支持。利用Tinker我們不僅可以用做bugfix,甚至可以替代功能的發(fā)布

類加載機制?

我們知道Android系統(tǒng)也是仿照java搞了一個虛擬機,不過它不叫JVM,它叫Dalvik/ART VM他們還是有很大區(qū)別的(這是不是我們的重點, 點開是個拓展閱讀)。我們只需要知道,Dalvik/ART VM 虛擬機加載類和資源也是要用到ClassLoader,不過Jvm通過ClassLoader加載的class字節(jié)碼,而Dalvik/ART VM通過ClassLoader加載則是dex。

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

PathClassLoader代碼位于libcore\dalvik\src\main\Java\dalvik\system\PathClassLoader.java?

DexClassLoader代碼位于libcore\dalvik\src\main\java\dalvik\system\DexClassLoader.java?

BaseDexClassLoader代碼位于libcore\dalvik\src\main\java\dalvik\system\BaseDexClassLoader.java

PathClassLoader

用來加載系統(tǒng)類和應(yīng)用類

DexClassLoader

用來加載jar、apk、dex文件.加載jar、apk也是最終抽取里面的Dex文件進行加載.

歸納一下就是:ClassLoader會遍歷這個數(shù)組,然后加載這個數(shù)組中的dex文件.?

而ClassLoader在加載到正確的類之后,就不會再去加載有Bug的那個類了,我們把這個正確的類放在Dex文件中,讓這個Dex文件排在dexElements數(shù)組前面即可.

為什么推薦Sophix?

為了提升產(chǎn)品在敏捷開發(fā)下的最佳發(fā)布體驗,分別嘗試了備受關(guān)注的阿里和微信兩大派系的熱更新方案(支付寶的Andfix和微信的Tinker),但在探索的過程中,發(fā)現(xiàn)兩種方案都存在弊端,如使用場景有限,修復(fù)成功率低,存在兼容問題。加之各方案還在內(nèi)部快速迭代,均未能達到商業(yè)化的標準,所以熱修復(fù)在項目中的應(yīng)用被暫時擱置了。

? ? ? ?直到最近,看到阿里推出了非侵入式熱修復(fù)框架Sophix。Sophix對其前輩Andfix,阿里百川Hotfix等方案進行了升級改造,打破了舊方案諸多限制,涵蓋了代碼修復(fù),資源修復(fù),So庫修復(fù)。加上阿里云平臺的支持,經(jīng)過簡單的配置就可接入使用,目之所及,Sophix已經(jīng)成為目前成熟度最高的熱修復(fù)框架。這也讓我重新燃起了對熱更新及底層技術(shù)探索的熱情。所以我想以此為契機,用系列文章的形式,圍繞熱點技術(shù)所涉獵的知識進行由淺入深的持續(xù)挖掘。

修復(fù)立即生效,是熱修復(fù)所追求的,底層替換方案通過在運行時利用hook操作native指針實現(xiàn)“熱”的特性。但這里有一個關(guān)鍵點,底層替換所操作的指針,實際上是ArtMethod,在類被加載,類中的每個方法都會有對應(yīng)的ArtMethod,它記錄了方法包括所屬類和內(nèi)存地址信息,Andfix正是通過篡改ArtMethod,將補丁方法ArtMethod的成員值逐一賦給舊方法,實現(xiàn)替換。問題就出現(xiàn)在這個逐一上。因為Andfix的ArtMethod方法結(jié)構(gòu)是根據(jù)Android開源代碼寫死的,面對國內(nèi)廠商的定制,經(jīng)常會導(dǎo)致兩者ArtMethod方法結(jié)構(gòu)不一致,這也是兼容問題產(chǎn)生的根本原因。

? ? ? ?為了解決這個問題,Sophix采用了對舊ArtMethod進行完整替換,通過動態(tài)測量ArtMethod的size(通過c層的mempy(dest ,src ,size)方法),進行全量拷貝。這樣做無論ArtMethod被修改成什么樣,只需要統(tǒng)一執(zhí)行拷貝,就可以完成替換,完全無視修改虛擬機導(dǎo)致的ArtMethod結(jié)構(gòu)差異.

?底層替換雖能使修復(fù)即時生效,但由于類加載后,方法結(jié)構(gòu)已固定,這就造成使用上會有諸多限制。相反類加載方案的使用場景更為廣泛。Sophix使用類加載作為兜底方案。在熱部署無法使用的情況下,自動降級為冷部署方案。

? ? ? ?無論是冷部署還是熱部署,都需要通過同一套補丁兼顧,在Art虛擬機下,默認支持多dex加載,虛擬機會優(yōu)先加載命名為classes.dex的文件。Sophix利用了這一點,將補丁文件命名為classes.dex,并對原有dex文件進行排序。這樣一來,art虛擬機就會先加載補丁文件,后續(xù)加載的同類名的類會被忽略,最后將加載得到的dexFile把dexElements整體替換。

? ? ? Dalvik虛擬機下情況則有些不同,Dalvik默認只加載classes.dex,其他dex則被忽略。那么,Sophix就需要一個全量dex,前面提到tinker采用自主研發(fā)的dexDiff技術(shù),從方法和指令的維度進行dex合成,但Dex合成過程發(fā)生在虛擬機堆內(nèi)存上,修復(fù)的成功率極大的受到性能問題的影響。為了解決這個問題,Sophix換了一種思路,從類的維度,對照補丁包中出現(xiàn)的類,在原有包中做刪除操作,如圖。

? ?為了避免刪除整個類信息而導(dǎo)致dex結(jié)構(gòu)發(fā)生偏移,所以只對舊包中類的入口進行刪除,實際上類的信息還在dex包中。這樣一來,冷啟動后,原有的類就不會被加載,相比Tinker的合成方案,Sophix的思路更為輕量化。

? ? ? ?至此,對Sophix對類文件修復(fù)的基本原理描述完畢。可以說Sophix吸取了百家之長,對問題的解決之法堪稱巧妙。但在驚嘆阿里技術(shù)底蘊的同時,也展現(xiàn)出底層技術(shù)的重要性,若沒有對虛擬機等底層技術(shù)的深耕探索,在系統(tǒng)框架的紛繁規(guī)則面前,也只能至于庭前止步。

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

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

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