一、熱修復技術作用
線上app BUG緊急修復,不重新發版,不重新安裝,在線遠程修復問題
二、局限性與適用場景
補丁只能針對單一客戶端版本,隨着版本差異變大補丁體積也會增大;
補丁不能支持所有的修改,例如AndroidManifest;
補丁無論對代碼還是資源的更新成功率都無法達到100%。
既然補丁技術無法完全代替升級,那它適合使用在哪些場景呢?
1. 輕量而快速的升級
2.遠端調試
3.數據統計
4.其他(Instant Run)
Android官方也使用熱補丁技術實現Instant Run。它分為Hot Swap、Warm Swap與Cold Swap三種方式。
三、熱修復的原理
1、通過更改dex加載順序實現熱修復
通過更改含有bug的dex文件的加載順序;在dex的加載中,若已找到方法則不會繼續查找,所以如果能讓修復之后的方法在含有bug的方法之前加載就能達到修復bug的目的。把有問題的類修復后,放到一個單獨的dex,通過反射插入到dexElements數組的最前面,讓虛擬機優先加載打完補丁的class。
實踐中,會發現運行加載類的時候報preverified錯誤,原來在DexPrepare.cpp,將dex轉化成odex的過程中,會在DexVerify.cpp進行校驗,驗證直接引用的類和clazz是否在同一個dex,如果是,則會打上CLASS_ISPREVERIFIED標志。通過在所有類(Application除外,當時還沒加載自定義類的代碼)的構造函數插入一個對在單獨的dex的類的引用,就可以解決這個問題。空間使用了javaassist進行編譯時字節碼插入。
隱患:虛擬機在安裝期間為類打上CLASS_ISPREVERIFIED標志是為了提高性能的,我們強制防止類被打上標志多少會影響app的性能。但是在大項目中拆分dex的問題已經比較嚴重,很多類都沒有被打上這個標志。
開源實現有Nuwa, HotFix, DroidFix。實際應用案例:QQ空間
2、通過Native替換方法指針的方式實現熱修復
主要是阿里開源的兩個熱修復框架:Dexpost AndFix。它們都是通過Native層使用指針替換的方法替換bug,達到修復bug的目的,具體可參考其github文章。
(1)基於Xposed的AOP框架,方法級粒度,可以進行AOP編程、插樁、熱補丁、SDK hook等功能。
Xposed需要Root權限,是因為它要修改其他應用、系統的行為,而對單個應用來說,其實不需要root。 Xposed通過修改Android Dalvik運行時的Zygote進程,並使用Xposed Bridge來hook方法並注入自己的代碼,實現非侵入式的runtime修改。
我們知道,應用啟動的時候,都會fork zygote進程,裝載class和invoke各種初始化方法,Xposed就是在這個過程中,替換了app_process,hook了各種入口級方法(比如handleBindApplication、ServerThread、ActivityThread、ApplicationPackageManager的getResourcesForApplication等),加載XposedBridge.jar提供動態hook基礎。
方法級的替換是指,可以在方法前、方法后插入代碼,或者直接替換方法。只能針對java方法做攔截,不支持C的方法。
硬傷是不支持art,不支持art,不支持art。
(2)AndFix同樣是方法的hook,AndFix不像Dexposed從Method入手,而是以Field為切入點。dalvik和art都支持
使用上,直接寫一個新的類,會由補丁工具會生成注解,描述其與要打補丁的類和方法的對應關系。
3、微信熱補丁方案
思想是全量替換新的Dex。即完全使用新的Dex,這樣既不出現Art地址錯亂的問題,在Dalvik也無須插樁。考慮到補丁包的體積,不能直接將新的Dex放在里面。但可以將新舊兩個Dex的差異放到補丁包中,最簡單可以采用BsDiff算法。
簡單來說,在編譯時通過新舊兩個Dex生成差異path.dex。在運行時,將差異patch.dex重新跟原始安裝包的舊Dex還原為新的Dex。這個過程可能比較耗費時間與內存,所以我們是單獨放在一個后台進程:patch中。為了補丁包盡量的小,微信自研了DexDiff算法,它深度利用Dex的格式來減少差異的大小。它的粒度是Dex格式的每一項,可以充分利用原本Dex的信息,而BsDiff的粒度是文件,AndFix/QZone的粒度為class。
在最極端的情況,由於利用了原本dex的信息完全替換一個13M的Dex,我們的補丁大小也僅僅只有6.6M。
但是這套方案並非沒有缺點,它帶來的問題有兩個:
(1)占用Rom體積;這邊大約是你修改Dex數量的1.5倍(dexopt與dex壓縮成jar)的大小。
(2)一個額外的合成過程;雖然我們單獨放在一個進程上處理,但是合成時間的長短與內存消耗也會影響最終的成功率。
微信的熱補丁方案叫做Tinker,也算緬懷一下Dota中的地精修補匠,希望能做到無限刷新。
若不care性能損耗與補丁包大小,QZone方案是最簡單且成功率最高的方案(沒有單獨的合成過程)。相對Tinker來說,它的占用Rom體積也更小。另一方面,QZone與Tinker的成功率大約相差3%左右。
事實上,一個完整的框架應該也是一個容易使用的框架。Tinker對補丁版本管理、進程管理、安全校驗等都有着很好的支持。同時我們也支持gradle與命名行兩種接入方式。希望在不久的將來,它可以很快的跟大家見面。
https://www.cnblogs.com/aademeng/articles/6883861.html
http://www.bbs0101.com/archives/1437.html
https://www.jianshu.com/p/52cacc0f23fa
https://www.cnblogs.com/fanfu1/p/5506149.html