轉載請註明出處:juejin.im/post/5a4462…java
從15年開始各技術大佬們開始研究熱修復技術,並陸續開源了許多的熱修復框架。如 Jasonross 的 Nuwa,美團的 Robust,阿里的 Andfix,騰訊的 Tinker 等等...均是Android 前輩們夜以繼日的成果。而如今熱修復被普遍地應用於Android 應用和遊戲,運用並理解熱修復框架在面試中也是加分項。因此,趕忙學起來吧...git
本文以Tinker 做爲學習對象,主要講述各開源框架的對比和記錄Tinker 的Demo 集成過程。github
這一節篇幅較長,主要是用本身的話來總結各熱修復框架的實現原理。若是想看Tinker接入實現的同窗可進入下一章節。Android 熱修復 - Tinker 實現及踩過的坑面試
最先看到熱修復框架的相關文章就是Qzone官方的文章,可是Qzone熱修復技術的實現代碼並無開源。不過GitHub上有一開源的熱修復框架Nuwa,實現原理和其類似。這裏咱們以Qzone爲例進行分析。算法
Qzone的實現原理是生成差分dex文件,將patch.dex插到dexElements的最前面。但patch.dex中的類patchA.java和classes.dex中的類classesB.java會相互引用,若是這兩個類所在的patch.dex和classes.dex不是同一個文件就會報錯。bash
疑惑的Qzone技術大佬發現classes.dex和classes2.dex也不是同一個文件,爲啥不報錯?因而繼續查,發現了這個判斷框架
if(!fromUnverifiedConstant && IS_CLASS_FLAG_SET(referrer, CLASS_ISPREVERIFIED)){
ide
若是被引用者(也就是classesB.java)這個類被打上了CLASS_ISPREVERIFIED標誌,那麼就會進行dex的校驗。校驗不過就報錯了。函數
那麼這個標誌是何時被打上去的? 在apk安裝的時候,虛擬機會將dex優化成odex後纔拿去執行。在這個過程當中會對全部class進行校驗。post
怎麼校驗的? 假設classesB.java類在它的static方法,private方法,構造函數,override方法中直接引用到classesC.java類。若是classesB.java類和classesC.java類在同一個dex中,那麼classesB.java類就會被打上CLASS_ISPREVERIFIED標記,被打上這個標記的類不能引用其餘dex中的類,不然就會報錯。因此要防止類被打上CLASS_ISPREVERIFIED的標誌。
// 校驗引用者 ClassB 和被引用者 PatchA 的 dex 是否相同,不一樣則報錯
if(referrer->pDvmDex != resClassCheck->pDvmDex &&
resClassCheck->classLoader != NULL)
複製代碼
如何防止類被打上CLASS_ISPREVERIFIED的標誌? Common類會被打包成單獨的hack.dex,這樣當安裝apk的時候,classes.dex內的類都會引用一個在不相同dex中的Common類,這樣就防止了類被打上CLASS_ISPREVERIFIED的標誌了,只要沒被打上這個標誌的類均可以進行打補丁操做。
優勢:
缺點:
Robust 插件對每一個產品代碼的每一個函數都在編譯打包階段自動的插入了一段代碼。經過判斷 if(changeQuickRedirect != null)
來肯定是否進行熱修復,當 changeQuickRedirect 不爲 null 時,調用 patch.dex 中同名類的同名方法達到 hotfix 的目的。
如何調用呢? 生成的patch.dex 中有兩個主要類,PatchesInfoImpl.java 和修復後的同名類 APatch.java。客戶端拿到patch.dex 後,用DexClassLoader 加載patch.dex,反射拿到PatchesInfoImpl.java 這個 class。並建立這個class 的一個對象。而後經過這個對象知道被替換的是誰,給它的變量changeQuickRedirect 賦值爲 patch.dex 中的 APatch 的對象,這樣就會去執行補丁包中的方法了。
大體流程圖以下所示:A_old 表示未被修復的原有類,A_new 表示已修復的新類。
優勢
缺點
Andfix 採用的方法是,在已經加載了的類中直接在 native 層替換掉原有方法,是在原來類的基礎上進行修改的。其核心在於 replaceMethod 函數,因此只支持方法替換,對於方法的增刪,資源更新,so 文件更新及類和屬性的替換等都是不支持的。
Andfix 的實現偏 native 層,筆者能力有限,其具體實現過程,就不妄加總結了。 更多實現細節請看 Andfix 官網文章
優勢
缺點
在 App 運行到一半的時候,全部須要發生變動的 Class 已經被加載過了,在Android 上是沒法對一個 Class 進行卸載的。而 Tinker 的方案,都是讓 Classloader 去加載新的類。若是不重啓,原來的類還在虛擬機中,就沒法加載新類。所以,只有在下次重啓的時候,在還沒走到業務邏輯以前搶先加載補丁中的新類,這樣後續訪問這個類時,就會 Resolve 爲新的類。從而達到熱修復的目的。
Tinker 的實現過程更像是在 Qzone 熱修復方案上作優化。核心點是性能最優,消耗最低。
經 Tinker 開發人員調研,Qzone 的方案最大挑戰在於性能,即Dalvik平臺存在插樁致使的性能損耗,Art平臺因爲地址偏移問題致使補丁包可能過大的問題。爲了不這兩個問題,根據 Instant Run 的全量替換新的 Dex 的思路,因而決定將新舊兩個Dex 的差別放到補丁包中。
通過調研,BsDiff 算法對 Dex 支持效果不太好,因此,Tinker 開發團隊人員自研了 DexDiff 算法。
最終, BsDiff 加載 so 和部分資源文件,DexDiff 加載 Dex文件,以達到性能最優。可是這個方案也有缺點,就是佔用 ROM 較大。好吧!如今手機內存都不小,多幾十 M 能夠接受。
優勢
缺點
Type | Nuwa | Robust | Andfix | Tinker |
---|---|---|---|---|
Company | Null | Meituan | Alibaba | Tencent |
開發時間 | 2015 | 2016 | 2015 | 2016 |
替換類 | √ | X | X | √ |
替換So | X | X | X | √ |
替換資源 | √ | X | X | √ |
即時生效 | X | √ | √ | X |
成功率 | 較高 | 最高 | 通常 | 較高 |
接口文檔 | ★★ | ★★ | ★★ | ★★★★ |
單就熱修復線上 APP 某一處或多處 bug 來講,Andfix能作到即時修復,且操做簡單,不用生成較多的 patch.dex 包,能輕鬆解決緊急問題。
但對於如非緊急 bug 的修復及小版本的發佈,對即時生效性要求不高的狀況,Tinker 支持的替換內容較豐富,更勝一籌。
阿里將 Andfix 升級爲商業版 SDK Sophix;騰訊將 Tinker 升級爲 Bugly。 Sophix 不但支持即時修復,還支持再次啓動修復類、so 文件、資源等。做爲商用 APP 集成 Sophix 是很好的選擇。 但 Tinker 的開源,爲其帶來了大量的使用者和測試者,除此之外還與各大手機廠商創建聯繫,使得各廠商在系統定製時也會考慮是否影響熱修復的問題。因此 Tinker 的兼容性可見一斑。
總的來講,各有千秋,各需所需吧。 咱們這裏以 Tinker 爲學習對象,接下來先讓 Tinker-Demo 跑起來,看一下實際效果。 Android 熱修復 - Tinker 實現及踩過的坑
推薦閱讀:Android 熱修復 - Tinker 實現及踩過的坑
Amigo學習(一)解決使用中遇到的問題
記錄在此,僅爲學習! 感謝您的閱讀!歡迎指正! 歡迎加入 Android 技術交流羣,羣號:155495090。