阿里百川HotFix的使用

熱修復技術前景分析

近兩年來,熱修復技術在安卓開發圈兒成爲焦點。隨之而來的是,相關的解決方案也不斷湧現。爲此,本文將熱修復的幾大流派分別做較深入的闡述,以使關注這一技術的開發同學有更深的瞭解。

在正式切入話題之前,我們先來看看傳統的開發流程究竟有哪些痛點。概括之,可以用三個“太”來描述:1.重新發布版本的代價太大;2.用戶下載安裝的成本太高;3.BUG修復不及時造成用戶體驗太差。

正因爲如此,熱修復技術才得以施展,並被廣大開發者追捧。那麼,熱修復開發流程具有怎樣的優勢?總結起來,也有三點。

第一, 無需重新發版,而且實時高效。

第二, 用戶對修復過程無感知,也無需下載新的應用,總之,代價非常小。

第三, 修復的成功率高,可以吧損失降至最低。

哎呀,熱修復技術真是棒棒噠。但是熱修復技術雖好,可不能“貪杯”,畢竟流派較多,哪款才適合呢?

市面上流行的熱修復技術對比

目前,市面上主流的幾個熱修復流派有這些,阿里andfix、美團Robust、QQ空間和微信Tinker。那,咱們就一起來看看這些技術方案都有哪些優缺點?

阿里andfix

hook本地方法. 並沒有整體替換class。1. 打開鏈接庫得到操作句柄, 獲取native層內部函數, 得到classobject對象. 2. 修復訪問權限屬性爲public 3. 得到新舊方法的指針, 新方法指向目標方法, 實現方法的替換

優點: 1. 不侵入打包, 性能無損耗;2. 即時生效。

缺點: 1. 需要針對dalvik虛擬機和art虛擬機做適配,需要考慮指令集的兼容問題,需要native代碼支持,兼容性上會有一定的影響; 2. 不支持新增類方法/字段,以及修改方法,也不支持對資源的替換。

美團Robust

類似Instant Run原理, 每個產品代碼的每個函數都在編譯打包階段自動的插入了一段代碼。

客戶端拿到patch.dex後,用DexClassLoader加載patch.dex. 其中的changeQuickRedirect字段賦值爲用patch.dex中的StatePatch.java這個class new出來的對象。這就是打patch的主要過程。

優點: 正常的使用DexClassLoader,兼容性高,未反射注入,實時生效。

缺點: 1. 原來能被ProGuard內聯的函數不能被內聯了,所以可能導致方法數的增加,原來沒超過65536但是後面可能就操作了65536限制,同時apk的體積也會一定程度的增大;2. so和資源的替換暫時不支持;3. 侵入式打包。

QQ空間

類似Multidex, 注入, 插樁. 大致的過程就是:把BUG方法修復以後,放到一個單獨的DEX裏,插入到dexElements數組的最前面,讓虛擬機去加載修復完後的方法。

davilk: 但是有一個問題是,當兩個調用關係的類不在同一個DEX時,就會產生異常報錯。我們知道,在APK安裝時,davilk虛擬機通過dexopt將classes.dex優化成odex文件,然後纔會執行。在這個過程中,會進行類的verify操作,如果調用關係的類都在同一個DEX中的話就會被打上CLASS_ISPREVERIFIED的標誌,然後纔會寫入odex文件。所以,爲了可以正常的進行打補丁修復,必須避免類被打上CLASS_ISPREVERIFIED標誌,具體的做法就是單獨放一個類在另外DEX中,讓其他類調用。但是雖然阻止了被打上CLASS_ISPREVERIFIED標誌, 但是運行時加載類做verify與optimize所以效率低下. 特別是應用剛啓動的情況下需要加載大量類的情況下就會花不少時間。

art: Art採用了新的方式,插樁對代碼的執行效率並沒有什麼影響。但是若補丁中的類出現修改類變量或者方法,可能會導致出現內存地址錯亂的問題。爲了解決這個問題我們需要將修改了變量、方法以及接口的類的父類以及調用這個類的所有類都加入到補丁包中。

優點: 兼容性高

缺點: 1. 不支持實時生效;2. avilk下類加載性能問題;3. art下補丁包包很大;4. 侵入式打包。

微信Tinker

dex merge,微信針對QQ空間超級補丁技術的不足提出了一個提供DEX差量包,整體替換DEX的方案。主要的原理是與QQ空間超級補丁技術基本相同,區別在於不再將patch.dex增加到elements數組中,而是差量的方式給出patch.dex,然後將patch.dex與應用的classes.dex合併,然後整體替換掉舊的DEX,達到修復的目的。

優點: 1. 自研DexDiff算法, 深度利用Dex的格式來減少差異的大小。它的粒度是Dex格式的每一項,可以充分利用原本Dex的信息,而BsDiff的粒度是文件 補丁包足夠小。2. 有效防止了qq空間導致的加載效率下降問題。3. 侵入式打包。

缺點: 1. 不支持即時生效;2. 需要給應用開啓新的進程才能進行合併,並且很容易因爲內存消耗等原因合併失敗;3. 合併時佔用額外磁盤空間,對於多DEX的應用來說,如果修改了多個DEX文件,就需要下發多個patch.dex與對應的classes.dex進行合併操作時,這種情況會更嚴重。

阿里百川HotFix

百川HotFix是在阿里AndFix的基礎上,增加了補丁管理後臺。我們可以在下面的圖中看到我們的服務後臺功能,可以上傳補丁。補丁必須跟版本號綁定,同時提供了補丁控制功能, 比如停止發佈/繼續發佈/灰度/全量發佈等功能。

同時HotFix基於手淘的實踐針對andfix做了大量優化,性能上提高了兼容和穩定性,功能上比如支持新增類和基於類方法作爲粒度所以有更小的補丁包,開源的andfix補丁包是以類作爲粒度。

事實上,阿里百川HotFix也在不斷演進之中,最新的2.0版已經突破了很多限制,比如,不支持資源修復,so修復;不支持新增類方法/類字段等。現在這些都不是問題啦,而且它還在依然在不斷進化!

相較於最初的1.X版本,阿里百川HotFix 2.0可謂發生了“翻天覆地”的變化。有哪些?請看。

  • 將1.x版本的所有限制全部取消;

  • 不僅僅只基於AndFix而是自由切換方案;

  • 不管資源/SO文件/類修復都能做到實時生效;

  • 傻瓜式接入, 完全不侵入你的打包過程, 可視化UI界面打補丁。

這麼一個好東東究竟啥時候有啊?別急,2017年1月中旬就會上線,到時候就可以“你有我有全都有了!”

另外,阿里百川HotFix還有一些“計劃”。

1、更小的補丁包,比如嘗試so和資源文件做bsdiff。

2、支持四大組件的代理。

3、更好的性能和穩定兼容性。

你以爲這樣就完了?No,下面再給大家分享一下阿里百川HotFix的一些具體修復方案。

百川Hotfix2.X 類修復方案

補丁工具檢測補丁冷部署or熱部署

  • 由於熱部署andfix修復正在運行的方法有crash的風險, 所以補丁工具提供參數由業務方來決定是否嘗試走熱部署, 如果用戶patch的方法沒有被高頻調用同時又有實時生效的需求, 那麼可以優先選擇走熱部署方案

熱部署 ->andfix支持的代碼變更

  • 此時走優化後的andfix方案

也就是目前hotfix1.0的方案

冷部署 ->andfix不支持代碼變更

  • davilk下hack本地方法native層繞過dvmresolveclass

patch dex追加到PathClassLoad的dexElements中, 同時我們知道插樁的解決方案會影響到運行時性能的原因在於:app內的所有類都預埋引用一個獨立dex的空類,導致安裝dexopt階段的preverify失敗,運行時將再次verify+optimize. 所以我們選擇了hack本地方法native層繞過dvmresolveclass方法的方式。

  • art下直接合成dex,採用手淘目前成熟的art動態部署方案

不同於微信tinker的dex merge方案, dex merge其實很佔用應用內存, 所以最終會導致dex merge失敗, 實際上art上默認已經支持多dex的合併, 我們只需要把patch dex跟原來apk中的dex合併成完整的新dex, 然後去替換PathClassLoad的dexElements即可.

阿里百川Hotfix2.X SO文件修復方案

  • art下預load原來so, 再load補丁so

  • davilk下預load補丁so, 再load原來的so

  • 關鍵: 綜合機型支持的abis和補丁包中的abis共同決定補丁so的新libPath

davilk和art下so文件加載的方式不一樣, 導致了需要區分art和davilk做不同的處理. 實際上我們還有另外一個so補丁的方案, 這裏暫時不對外透露

更好的性能

  • SOPatchManager.load(String libPath) ->代替 System.load(String pathName)

  • SOPatchManager.loadLibrary(String libName) ->代替 System.loadLibrary(String libName)

我們知道一個so文件如果load兩次那麼本地內存的使用會變大. 所以我們提供了替代System加載so文件的方法, 我們建議所有的so文件加載都通過這個方法, 那麼加載so文件的時候只會嘗試去加載指定目錄下去的補丁so, 而不會去加載安裝apk中的so文件

阿里百川Hotfix2.X 資源文件修復方案

Android資源文件的特點

  • 資源id編碼於resources.arsc文件中,排布緊密。按照排布順序進行自動編號

  • res目錄保存所有帶id的資源文件。佈局文件爲二進制形式的xml文件,xml以資源id的方式引用其他資源

  • assets目錄存放所有原始文件,不帶id

  • aapt進行資源的構造,包括自動分配資源id與R文件的生成,默認情況下,每次編譯不保證和之前包中的id一致

目前市面上普遍採用的三種方式。

  • 差量合成完整的資源包,運行時完整加載資源。 缺點:合成資源佔用時間和內存,容易引起卡頓。

  • 修改aapt,對以後可能新增的資源提前留空,運行時patch包中新增資源id對應留出的位置。 缺點:需改變打包流程,修改代碼並編譯替換sdk中的aapt。打包侵入太強,且留空佔用一定磁盤空間。留空多少是預先定好的,無法改變。

  • 插件化,組件化資源。 缺點:資源需要劃分模塊,提前規劃。殺雞焉用牛刀?

一個優秀的資源熱修復方案應該做到:

  • 補丁包儘可能地小。加載補丁迅速,性能好,內存和時間消耗極小。

  • 不改變打包流程,保持sdk工具鏈的完整性。

  • 開發透明,開發者無感知。不需要事先固定資源id。

  • 方便易用,傻瓜式操作。一鍵完成patch工作。

阿里百川資源熱修復

  • 直接基於新舊兩個apk來構造補丁包,不需要改造aapt,對編譯過程無要求。

  • 精確比較各個資源id的使用情況,最大程度利用原先基線包資源,補丁包中只包含新增和修改的資源。

  • 運行時無需合成操作,快速應用生效。不影響性能。

  • 不僅僅是簡單修復,對於任意程度、乃至天翻地覆的修改都能適用。只是補丁文件會比較大。

  • 使用方便,只需要選取新舊兩個apk,一鍵生成補丁。

  • 兼容Android所有機型,穩定性好。

  • 配合類修復方案, 我們能夠做到資源修復的實時生效

需要注意的地方

  • 如果事先自己做了資源混淆,需要保證新舊包混淆的關係保持一致,否則打補丁時會找不到原來基線包中資源,而將非新增資源視爲新增資源,導致補丁包變大。

  • 建議每次打包時設置去除無用的資源。這樣即可以減小包大小,同時也保證補丁包中新增資源都是有用的。

  • AndroidManifest中引用的資源無法改變。有些資源如icon是安裝時固定的,目前所有補丁方案都無法進行改變。而另一些資源,如Theme,我們可以提取AndroidManifest中的資源信息,通過代碼的方式進行設置。

阿里百川HotFix管理後臺服務

  • 補丁灰度發佈/正式發佈

發佈前可以通過本地/掃碼兩種方式驗證之後再發布上線, 本地補丁模式是指補丁可以放到任何一個指定的目錄下即可. 掃碼模式是掃描二維碼生成一個下載url, 然後直接下載這個時候不需要和服務器驗證身份. 灰度發佈指定具體的用戶數然後隨機推送

  • 補丁回滾

回滾到目標補丁版本, 所有該應用版本下的設備都會回滾到目標補丁的版本。

  • 補丁安全
  1. 平臺託管RSA祕鑰 2. 補丁加載安全簽名校驗

我們後續提供的服務

  • 補丁自定義平臺無關AES祕鑰

更安全, 此時打補丁的時候用戶可以填入自定義AES祕鑰, 然後SDK初始化的時候填入這個祕鑰即可. 我們阿里百川平臺完全不知道你的祕鑰, 所以你們的補丁在我們的後臺是絕對安全的.

  • 補丁條件下發
  1. 分系統版本 比如一個bug只在android5.0上覆現, 那麼可能只想對android5.0下發補丁

  2. 分渠道 比如只想對某個具體的渠道, 豌豆莢/小米不同的渠道進行分發

  3. 自定義TAG 上述幾個是默認提供的條件, 當然我們提供了更加自由的方式, 你可以對任何一個補丁打tag, 然後客戶端只能請求下載到後臺指定tag的補丁.

  • 實時顯示補丁加載成功率等數據

後續可能會上報補丁加載失敗詳情, 方便排查問題.

  • 一鍵清除補丁

使用回滾功能必需要具備一下幾個條件:1. 當前的版本已停止發佈 2. 該版本之前存在至少一個歷史版本 所以如果第一個補丁就下發錯誤的話, 補丁回滾就無能爲力了, 所以我們提供一鍵清除補丁的功能。

熱修復技術對比

對比完這些熱修復技術是不是覺得Hotfix是不錯的選擇。接下來就一起來了解一下hotfix的接入和使用吧。

接入流程大概如下:
1. 註冊阿里百川開發者帳號(這個用來對阿里百川一些sdk的接入進行管理)
2. 新建一個項目,並開通阿里百川hotfix的使用權限。
3. 下載,集成sdk。

Hotfix SDK接入

HotFix項目文檔地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章