本文彙總了主流的熱修復框架,並對每個框架的特性做了簡單的介紹,主流方案比較如下圖。
方案對比 | Andfix | 阿里百川HotFix | Sophix | 微信Tinker | 餓了麼Amigo | 美團Robust |
---|---|---|---|---|---|---|
即時生效 | yes | yes | 同時支持即時生效和冷啓動修復 | no | no | yes |
方法替換 | yes | yes | yes | yes | yes | yes |
so替換 | no | no | yes | yes | yes | no |
資源替換 | no | no | yes | yes | yes | no |
四大組件 | no | no | yes | no | yes | no |
實現原理 | 方法替換jni Hook | 方法替換jni Hook | 組合多種方法 | 修改dexElements數組 | 直接替換classLoader | java hook |
補丁生成 | 依賴apkpatch工具 | SophixPatchTool界面工具 | 便捷,圖形化界面 | gradle命令生成 | 新Apk即是補丁文件 | gradle生成 |
補丁大小 | 較小 | 較小 | 不大,僅變動的資源和代碼 | Davilk全量較大,Art差異包 | 較大 | 較小 |
接入成本 | 低 | 低 | 低 | 一般 | 低 | 高 |
性能開銷 | 較小 | 較小 | 不大,僅變動的資源和代碼 | 在App裏合成,較大 | 全量替換,較大 | 較小 |
服務器支持 | 無 | 有,支持加密傳輸及簽名校驗 | 支持服務端控制 | TinkerPatch平臺 | 有,已停止更新 | 無 |
Andfix
原理
方法替換是 AndFix 的熱修復方案的關鍵,虛擬機在加載一個類的時候會將類中方法解析成 ArtMethod 結構體,結構體中保存着一些運行時的必要信息以及需要執行的指令指針地址。那麼我們只要在 native 層將原方法的 ArtMethod 結構體替換成新方法的結構體,那麼執行原方法的時候便會執行到新方法的指令,完成了方法替換。
AndFix提供了一種Native層hook Java層代碼的思路,實現了動態的替換方法。在處理簡單沒有特別複雜的方法中有獨特的優勢,但因爲在加載類時跳過了類裝載過程直接設置爲初始化完畢,所以不支持新增靜態變量和方法。
集成方法
1.加載初始化PatchManager
2.添加patch文件:patchManager.addPatch(patchPath)
AndFix只需初始化庫函數即可,對原有代碼無侵入,使用簡單。
補丁文件的生成(需要apkpatch工具)
1.準備新舊apk包 a.apk b.apk
2.下載apkpatch工具,運用工具生成patch
C:\apkpatch-1.0.3>apkpatch.bat -f a2.apk -t a1.apk -o out_dir -k keystore_path -p keystore_password -a alias -e entry_password
3.把patch文件推送給手機放到手機SD卡上執行升級
補丁文件裏面包含APP簽名,PATCH.MF文件裏Patch_Classes字段包含了需要修復的類的名稱信息。
優缺點
優點:集成簡單,對App性能基本無影響,無需重啓App可及時生效。
缺點:不能新增方法,兼容性不能保證。
傳統的底層替換方式,不論是Dexposed、Andfix或者其他安全界的Hook方案,都是直接依賴修改虛擬機方法實體的具體字段。例如,改Dalvik方法的jni函數指針、改類或方法的訪問權限等等。這樣就帶來一個很嚴重的問題,由於Android是開源的,各個手機廠商都可以對代碼進行改造,而Andfix裏ArtMethod的結構是根據公開的Android源碼中的結構寫死的。如果某個廠商對這個ArtMethod結構體進行了修改,就和原先開源代碼裏的結構不一致,那麼在這個修改過了的設備上,通用性的替換機制就會出問題。這便是不穩定的根源。
AndFix原理分析
Android熱修復框架AndFix原理解析及使用
阿里百川HotFix
阿里百川Hotfix解決方案是基於andfix方案的一個升級擴展,更加簡化了接入流程,並加入了服務管理平臺,可在阿里雲服務器上下發更新補丁,並實時查看補丁更新情況。
Sophix
Sophix是阿里熱修復的最新版本,提供了一套更加完美的客戶端服務端一體的熱更新方案,做到了圖形界面一鍵打包、加密傳輸、簽名校驗和服務端控制發佈與灰度功能,讓你用最少的時間實現最強大可靠的全方位熱更新。
官方接入參考
業界首個非侵入式熱修復方案Sophix重磅推出,顛覆移動端傳統更新流程
微信Tinker
原理
類替換
Tinker原理是通過classLoader機制修改pathclassloader中的dexElements數組順序,把修復好的dex包加到原有dexElements數組前面來達到類替換的目的。
在App啓動時通過dexclassloader讀取補丁dex文件,用反射的方法獲取到dexElements數組,同時拿到系統的pathclassLoader的dexElements,兩者進行合併排序,把patch的相關dex信息放在數組前端,最後合併數組結果賦值給pathList保證classloader優先到patch中查找加載。
爲什麼要繼承ApplicationLike類?
通常我們都是在Application中進行一些初始化的工作,包括tinker的初始化,那麼application中所涉及到的類,在tinker初始化完成前就已經被類加載器所加載了,那麼我們之前所說的通過將我們的補丁dex包插入到dexElements數組的前段的方法就不起作用了,Tinker推薦我們將自己的Application繼承自ApplicationLike,在該類中先進行dex替換後,在加載application對象,同時tinker也就支持application的修復了。
集成方法
application需要繼承applicationLike,代碼有一定侵入性。
1.新dex與舊dex通過dex差分算法生成差異包 patch.dex
2.將patch dex下發到客戶端,客戶端將patch dex與舊dex合成爲新的全量dex
3.將合成後的全量dex 插入到dex elements前面(此部分和QQ空間機制類似),完成修復
補丁生成
通過gradlew tinkerPatchRelease命令生成,需配置gradle參數,需要上一個版本的安裝包用來比較差異。會自動將 apk,mapping 和 R 文件複製到tinker-old文件夾中。
補丁文件生成通過自研DexDiff算法實現,既解決了Dalvik平臺的性能損耗問題,又解決了Art平臺補丁包過大的問題。但這套方案的缺點在於佔Rom體積比較大,微信考慮到移動設備的存儲空間提升比較快,增加幾十M的Rom空間這個代價可以接受。同時採用分平臺合成的想法,即在Dalvik平臺合成全量Dex,在Art平臺合成需要的小Dex。
1.Dalvik全量合成,解決了插樁帶來的性能損耗;
2.Art平臺合成small dex,解決了全量合成方案佔用Rom體積大的問題;
3.大部分情況下Art.info僅僅1-20K, 解決由於補丁包可能過大的問題;
優缺點
支持資源替換,so替換,兼容性好。
不支持修改AndroidManifest.xml,不支持四大組件的代理,沒有Crash 啓動保護 。
Dex合併內存消耗在vm head上,容易OOM,最後導致合併失敗。
如果本身app佔用內存已經比較高,可能容易導致app被系統殺掉。
github
Wiki
TinkerPatch 平臺
微信Tinker的一切都在這裏,包括源碼
Tinker Application代理機制
簡單易懂的tinker熱修復原理分析
餓了麼Amigo
原理
Amigo SDK是針對整個APP進行修復,不只是針對某個dex,又或者某個資源,又或者某個so文件。隨意添加組件activity、receiver、service也是支持的。
Amigo 原理與 QQZone 的方案有些類似,QQZone,Tinker,Nuwa這類方案是通過修改PathClassLoader中的dex實現的,Amigo則是釜底抽薪直接替換ClassLoader。同時進一步實現了 so 文件、資源文件、四大組件的修復.
集成方法
集成簡單,代碼無侵入性。
Amigo.workLater(context, patchApkFile, callback);
Amigo.work(context, patchApkFile);
補丁生成
無需單獨生成,新Apk即是補丁包,全量替換。
優缺點
兼容性好,支持更多的修復方式。集成簡單。
補丁較大,佔用資源較多。
美團Robust
原理
參考了 Instant Run 熱插拔原理。
Robust插件對每個產品代碼的每個函數都在編譯打包階段自動的插入了一段代碼,插入過程對業務開發是完全透明。
編譯打包階段自動爲每個class都增加了一個類型爲ChangeQuickRedirect的靜態成員,而在每個方法前都插入了使用changeQuickRedirect相關的邏輯,當 changeQuickRedirect不爲null時,可能會執行到accessDispatch從而替換掉之前老的邏輯,達到fix的目的。由於使用的是最基礎的DexClassLoade並沒有更改原有的代碼邏輯,也就不存在兼容性問題。
集成方法
1.集成了 Robust 後,生成 apk。保存期間的混淆文件 mapping.txt,以及 Robust 生成記錄文件 methodMap.robust
2.使用註解 @Modify 或者方法 RobustModify.modify() 標註需要修復的方法
3.開啓補丁插件,執行生成 apk 命令,獲得補丁包 patch.jar
4.通過推送或者接口的形式,通知 app 有補丁,需要修復
5.加載補丁文件不需要重新啓動應用
代碼修改需要添加註釋,修改,標記。集成對原有代碼有侵入,接入成本較高。
補丁生成
Robust補丁自動化,爲Robust自動生成補丁,使用者只需要提交修改完bug後的代碼,運行和線上apk打包同樣的gradle命令即可,會在項目的app/build/outputs/robust目錄下生成補丁。
優缺點
優點:
幾乎不會影響性能(方法調用,冷啓動)
支持Android2.3-8.x版本
高兼容性(Robust只是在正常的使用DexClassLoader)、高穩定性,修復成功率高達99.9%
補丁實時生效,不需要重新啓動
支持方法級別的修復,包括靜態方法
支持增加方法和類
支持ProGuard的混淆、內聯、優化等操作
缺點:
代碼是侵入式的,會在原有的類中加入相關代碼
so和資源的替換暫時不支持
會增大apk的體積,平均一個函數會比原來增加17.47個字節,10萬個函數會增加1.67M。
會增加少量方法數,使用了Robust插件後,原來能被ProGuard內聯的函數不能被內聯了
GitHub
GitHub Wiki
Android 美團Robust熱更新 使用入門
補充
SO庫熱修復方案
so庫的修復本質是對native方法的修復和替換,和類加載方案類似,可以把補丁so庫的路徑插入到nativeLibraryDirectories數組的最前面,使得優先加載補丁庫而不是原來的庫來達到修復目的。
資源替換參考Instant run
1.通過反射替換掉原有的AssetManager
2.找到引用了原AssetManager對象的字段並替換爲新的引用。