天天看點

阿裡HotFix2.0更新詳解 暢談熱修複領域那些事摘要:

摘要:

熱修複領域充斥着各大流派,如阿裡AndFix、美團Robust、QQ空間、微信Tinker等,每種方法各有優劣。本文所介紹的阿裡Hotfix 2.x是在1.x版本進行了優化和創新,不僅支援靈活切換熱部署和冷部署的方案;同時,實作了資源、SO檔案、類修複的實時生效;整體接入過程采用傻瓜式方法,完全不侵入打包過程,為使用者提供了可視化的UI界面。

在阿裡HotFix2.0更新詳解直播中,阿裡HotFix核心開發工程師悟二從熱修複背景、常見的熱修複方案、阿裡HotFix曆程及2.0的突破與創新三個方面展開了詳細的演講。分享中,他重點介紹了阿裡Hotfix2.X 類、SO檔案、資源檔案修複方案以及管理背景服務,并對阿裡Hotfix 2.X将來需要新增的功能也做了展望。

熱修複背景

阿裡HotFix2.0更新詳解 暢談熱修複領域那些事摘要:

正常Bug修複流暢包括版本上線、使用者安裝、發現Bug、緊急修複、重新發版、使用者安裝六步。該流程中存在着明顯的不足,首先重新釋出版本代價太大,由于APK是通過多管道釋出的,重新發版時,需要再次對每個管道推送新的APK包;其次,使用者下載下傳安裝成本也随之增高;此外,由于需要重新發版、安裝,導緻Bug修複不及時,使用者體驗較差。

熱修複流程的前四步同正常修複流程相同,但在熱修複中,不再進行重新發版,取而代之的是生成上傳更新檔;此後,SDK拉取加載更新檔,完成Bug修複。在熱修複過程中,無需重新發版,能夠實時高效熱修複;同時實作了使用者無感覺修複,無需下載下傳新的應用,代價小。相比于正常修複過程,熱修複的成功率更高,将損失降到了最低。

熱修複的幾大流派

常見的熱修複流派包括阿裡AndFix、美團Robust、QQ空間、微信Tinker等,下面來一一介紹。

阿裡AndFix

阿裡HotFix2.0更新詳解 暢談熱修複領域那些事摘要:

阿裡andfix 熱修複方案通過Hook本地方法. 并沒有整體替換class,整體流程如下:第一步,打開連結庫得到操作句柄, 擷取native層内部函數, 得到classobject對象;第二步,修複通路權限屬性為Public ;第三步,得到新舊方法的指針,新方法指向目标方法,實作方法的替換。

整個過程中不侵入打包,性能無損耗;同時可以即時生效。其缺點同樣明顯:相容性方面很不穩定,需要針對Dalvik虛拟機和Art虛拟機做适配;不支援新增類方法/字段,以及修改方法,也不支援對資源的替換;運作時方法被Patch,有Crash風險。

美團Robust

美團熱修複方案Robust 的原理類似Instant Run,每個産品代碼的每個函數都在編譯打包階段自動的插入了一段代碼。用戶端拿到Patch.Dex後,用DexClassLoader加載Patch.Dex。其中的changeQuickRedirect字段指派為用Patch.Dex中的StatePatch.java這個Class New出來的對象。

在整個打Patch過程中,該方案正常的使用DexClassLoader,相容性高;未反射注入,能夠實時生效。該方案的缺點在于:因為在每端函數前插入代碼,需要侵入打包過程;原來能被ProGuard内聯的函數不能被内聯了,是以可能導緻方法數的增加,可能會超過65536限制,同時也會導緻APK體積增大;該方案不支援SO檔案和資源檔案的修複。

手機QQ空間

阿裡HotFix2.0更新詳解 暢談熱修複領域那些事摘要:

手機QQ空間熱修複方案可以用注入和插樁概括。其大緻過程是:把Bug方法修複之後,放到一個單獨的DEX内,插入到Dex Elements數組的最前面,讓虛拟機去加載修複完後的方法。

該方案類似谷歌的Multidex ,在保障穩定性的前提下相容性很高。缺點是:不支援實時生效;在Davilk下,類加載存在性能問題;Art下,更新檔包涵有類、父類以及引用該類的所有類,是以更新檔包較大;由于原DEX中的類需要引用額外的DEX類,需要侵入式打包。

微信Tinker

微信Tinker為了解決QQ空間更新檔技術由于插樁帶來的效率問題,引入DEX差量包。其主要的原理與QQ空間超級更新檔技術基本相同,最大差別在于:不再将Patch.dex增加到Elements數組中,而是差量的方式給出Patch.Dex;然後将Patch.Dex與應用的Classes.Dex合并,然後整體替換掉舊的DEX,達到修複的目的。

該方案中通過自研DexDiff算法,深度利用Dex的格式來減少差異的大小,進而做到更新檔包足夠小。其缺點在于:不支援實時生效;由于更新檔DEX需要和原DEX合并,需要占用額外記憶體和磁盤空間,并且很容易因為記憶體消耗等原因合并失敗;與QQ空間更新檔技術相同,同樣需要侵入式打包。

阿裡HotFix2.0更新詳解 暢談熱修複領域那些事摘要:

上圖是Hotfix和主流的熱修複方案的效果對比。可以看出,在即時生效、性能消耗、Rom體積、接入複雜度、更新檔包大小、類替換、SO檔案替換、資源方案等方面,Hotfix都具有相對的優勢。

阿裡Hotfix 1.x版本

阿裡HotFix2.0更新詳解 暢談熱修複領域那些事摘要:

阿裡Hotfix 1.x在AndFix的基礎上,增加了更新檔管理背景;同時基于手淘的實踐,針對AndFix做了大量優化, 性能上提高了相容和穩定性;功能上支援新增類并提供了更小的更新檔包(這是因為基于類方法作為粒度)。

從圖中可以看到阿裡Hotfix 1.x服務背景功能,使用者可以建立應用版本,然後根據版本号上傳更新檔;此外,還提供了更新檔控制功能,比如停止釋出、繼續釋出、灰階/全量釋出等功能。

但阿裡Hotfix 1.x仍存在很多限制:

不支援資源、So檔案修複;不支援新增類方法/類字段,這是因為Hotfix 1.x本質上是Hook一個已存在的的方法;

參數包括Long、Double、Float基本類型的方法不能被Patch,同時參數超過8的方法不能被Patch;

被反射調用的方法不能被Patch,具體來說是非靜态方法的反射調用會提示IllegalArgumentException 異常,當靜态方法被反射調用,如果反射調用不涉及類對象,則可以被Patch;

構造方法不能被Patch,實際上不允許修改一個類字段(包括靜态的和非靜态的);

正在運作的方法不能被Patch,也就是說如果一個方法正在運作,然後方法的在Native層的結構被替換, 那麼就很可能導緻Crash。

阿裡Hotfix2.0方案

阿裡HotFix2.0更新詳解 暢談熱修複領域那些事摘要:

相比于1.X版本,阿裡Hotfix 2.x版本将上面的限制完全取消,不僅僅隻基于AndFix,而是靈活切換熱部署和冷部署的方案;實作了資源、SO檔案、類修複的實時生效,同時采用了傻瓜式接入方案,完全不侵入打包過程,對使用者提供了可視化的UI界面打更新檔。

阿裡Hotfix 2.x版本由于相比于1.x版本,變更很大,,是以需要預先做大規模的內建測試才會正式上線。

阿裡Hotfix2.X 類修複方案

Hotfix2.X在熱修複過程中是不侵入打包過程的,而是通過更新檔工具生成更新檔。由于熱部署Andfix修複正在運作的方法有Crash的風險, 是以更新檔工具提供參數由業務方來決定是否嘗試走熱部署;如果使用者Patch的方法沒有被高頻調用同時又有實時生效的需求,那麼可以優先選擇走熱部署方案;但這并非絕對,當代碼變更導緻熱部署不支援時,還是會轉向冷部署。

熱部署

熱部署就是AndFix支援的代碼變更,此時走優化後的AndFix方案,也就是Hotfix1.X方案。

冷部署

冷部署就是AndFix不支援的代碼變更。冷部署針對Davilk和Art分别做了不同的處理:

  • Davilk下,注入追加到PathClassLoader的dexElements,但無需不插樁,通過Hack本地方法進而繞過dvmresolveclass;
  • Art下直接合成一個完整dex,采用手淘目前成熟的art動态部署方案,最後替換PathClassLoader的dexElements即可。

阿裡Hotfix2.X SO檔案修複方案

  • Davilk和ART下SO檔案加載的方式不一樣,導緻了需要區分Art和Davilk做不同的處理:
  • ART下預Load原來的SO檔案,再加載更新檔SO檔案;
  • Davilk下預Load更新檔SO檔案,再加載原來的SO檔案。

這裡的關鍵是:綜合機型支援的Abis和更新檔包中的Abis共同決定更新檔SO的新LibPath。這兩種加載方式都需要對加載兩次SO檔案,勢必會增加一次本地記憶體的消耗,是以為了達到更好的性能,在Hotfix2.X中提供了下面兩個接口替換掉系統加載SO檔案的接口:

  • SOPatchManager.load(String libPath) 代替 System.load(String pathName)
  • SOPatchManager.loadLibrary(String libName)代替 System.loadLibrary(String libName)

阿裡Hotfix2.X 資源檔案修複方案

在資源檔案中,資源ID編碼于Resources.arsc檔案中,排布緊密,并按照排布順序進行自動編号;RES目錄儲存所有帶ID的資源檔案。布局檔案為二進制形式的XML檔案,XML以資源ID的方式引用其他資源;Assets目錄存放所有原始檔案,不帶ID;Aapt進行資源的構造,包括自動配置設定資源ID與R檔案的生成,預設情況下,每次編譯不保證和之前包中的ID一緻。

目前市面上普遍采用的三種資源修複方案:

  • 差量合成完整的資源包,運作時完整加載資源;這種方案的缺點是:合成資源占用時間和記憶體,容易引起卡頓。
  • 修改aapt,對以後可能新增的資源提前留白,運作時Patch包中新增資源ID對應留出的位置;該方案的缺點是:需改變打包流程,修改代碼并編譯替換SDK中的Aapt;打包侵入太強,且留白占用一定磁盤空間。留白多少是預先定好的,無法改變。
  • 插件化,元件化資源;這種方式的缺點是:資源需要劃分子產品,提前規劃。

百川資源檔案修複方案直接基于新舊兩個APK來構造更新檔包,不需要改造AAPT,對編譯過程無要求;同時,精确比較各個資源ID的使用情況,最大程度利用原先基線包資源,更新檔包中隻包含新增和修改的資源;在運作時無需合成操作,快速應用生效,不影響性能。

百川資源檔案修複方案不僅僅是簡單修複,對于任意程度、乃至天翻地覆的修改都能适用,但更新檔檔案會比較大,該方案相容Android所有機型,隻需選取新舊兩個APK,一鍵快捷生成更新檔,并且穩定性較好;配合類修複方案,能夠做到資源修複的實時生效。在使用百川資源檔案修複方案,需要注意以下地方:

(1)如果事先自己做了資源混淆,需要保證新舊包混淆的關系保持一緻,否則打更新檔時會找不到原來基線包中資源,而将非新增資源視為新增資源,導緻更新檔包變大。

(2)建議每次打包時設定去除無用的資源。這樣即可以減小包大小,同時也保證更新檔包中新增資源都是有用的。

(3)AndroidManifest中引用的資源無法改變。有些資源如icon是安裝時固定的,目前所有更新檔方案都無法進行改變。而另一些資源,如Theme,我們可以提取AndroidManifest中的資源資訊,通過代碼的方式進行設定。

阿裡Hotfix管理背景服務

阿裡Hotfix背景目前提供的服務主要有:更新檔灰階釋出/正式釋出、更新檔復原和更新檔安全。

  • 更新檔灰階釋出/正式釋出,在釋出前可以通過本地或掃碼兩種方式驗證之後再釋出上線; 本地更新檔模式是指更新檔可以放到任何一個指定的目錄下即可;掃碼模式是掃描二維碼生成一個下載下傳URL,然後直接下載下傳,此時不需同伺服器驗證身份;灰階釋出指定具體的使用者數然後随機推送。
  • 更新檔復原是指發生錯誤時可以復原到目标更新檔版本,同時該應用版本下的所有裝置都會復原到目标更新檔的版本。
  • 更新檔安全方面,HotFix背景托管了RSA密鑰,同在在更新檔加載時需要進行安全簽名校驗。

在不遠的将來未來,将在阿裡HotFix平台上推出以下服務:

  • 更新檔自定義平台無關AES秘鑰。在打更新檔時,使用者可以自定義AES密碼,然後在SDK初始化時填入這個秘鑰即可。阿裡百川平台對該秘鑰完全無感覺,做到更新檔在背景的絕對安全。
  • 更新檔條件下發,支援分系統版本、分管道以及自定義TAG的方式下發更新檔到目标位置。
  • 實時顯示更新檔加載成功率等資料,後續可能會上報更新檔加載失敗詳情, 友善排查問題。
  • 一鍵清除更新檔,使用復原功能必需要具備兩個條件:①目前的版本已停止釋出;②- 該版本之前存在至少一個曆史版本。是以如果第一個更新檔就下發錯誤的話,更新檔復原就無能為力了,是以需要提供一鍵清除更新檔的功能。

在Swift Hot Patch上的進展

在Swift 3推出之後,也是有越來越多的項目轉向了Swift。那麼,在業界技術分享言必稱Swift的時代,Hot Patch對Swift的支援情況又是怎樣呢?

阿裡HotFix2.0更新詳解 暢談熱修複領域那些事摘要:

目前業界已有兩種成熟的産品:WAX和JSPatch。前者是基于Lua語言的Patch方案;後者是基于JavaScript語言的Patch方案。兩者都依托于Objective-C的Runtime,通過Method Swizzling将待Patch方法的實作替換為_objc_msgForward/_objc_msgForward_stret,再替換forwardInvocation:為特定語言的橋接方法來調用更新檔實作。

由于兩者都強依賴Objective-C的Runtime和NSObject的forward機制,它們在支援Swift Hot Patch時就會受到相應限制。

阿裡HotFix2.0更新詳解 暢談熱修複領域那些事摘要:

相比于WAX和JSPatch,Rollout.io是更面向Swift的Hot Patch方案。它在編譯器swiftc前增加了代碼注入邏輯,修改swift相關的代碼。這樣,swiftc在編譯時使用的就是經過注入的代碼了。這裡以viewDidLoad方法為例,說明代碼的注入:首先,根據方法ID取出Patch所需的資料;接着,判斷是否應該Patch;如果應該Patch,則以Patch資料、target、方法入參和方法調用closure為參數執行Patch。經過注入,Patch就可以替代任何方法的原始實作。

然而,Rollout.io的實作有很多問題:它支援的Patch非常受限,隻能patch class instance function;此外,它并不支援在Patch中調用Swift方法,調用Objective-C方法時,它的文法也非常繁瑣。

阿裡HotFix2.0更新詳解 暢談熱修複領域那些事摘要:

HotFix的Swift Hot Patch融合了WAX/JSPatch和Rollout.io的優長,并補足了Rollout.io在Patch方面的短闆。具體來說,除了在class function patch時沒有WAX/JSPatch和Rollout.io的諸多限制外,也同時支援global function、struct function和enum function的Patch。在調用Objective-C方法時,維持了簡短的文法;同時,也正在研究動态調用Swift方法的方式。

阿裡HotFix2.0更新詳解 暢談熱修複領域那些事摘要:

SteveMcConnell在《CodeComplete》中曾說過“Program into your language, not in it”。希望Swift Hot Patch工具能夠幫助更多的開發者深入Swift,自由地使用語言去表達自己的思想,而不隻是停留在文法層面。

繼續閱讀