本文彙總了主流的熱修複架構,并對每個架構的特性做了簡單的介紹,主流方案比較如下圖。
方案對比 | 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平台 | 有,已停止更新 | 無 |
Android熱修複技術原理詳解
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即是更新檔包,全量替換。
優缺點
相容性好,支援更多的修複方式。內建簡單。
更新檔較大,占用資源較多。
Amigo 源碼解讀
Amigo Github
美團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對象的字段并替換為新的引用。