共計 4924 個字符,預計需要花費 13 分鐘才能閱讀完成。
這篇文章主要介紹了 Android 熱補丁怎么理解的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇 Android 熱補丁怎么理解文章都會有所收獲,下面我們一起來看看吧。
一. 為什么需要熱補丁
熱補丁:讓應用能夠在無需重新安裝的情況實現更新,幫助應用快速建立動態修復能力。
從上面的定義來看,熱補丁節省 Android 大量應用市場發布的時間。同時用戶也無需重新安裝,只要上線就能無感知的更新。看起來很美好,這是否可以意味我們可以盡量使用補丁來代替發布呢?事實上,熱補丁技術當前依然存在它的局限性,主要表現在以下幾點:
補丁只能針對單一客戶端版本,隨著版本差異變大補丁體積也會增大;
補丁不能支持所有的修改,例如 AndroidManifest;
補丁無論對代碼還是資源的更新成功率都無法達到 100%。
既然補丁技術無法完全代替升級,那它適合使用在哪些場景呢?
二. 輕量而快速的升級
熱補丁技術也可以理解為一個動態修改代碼與資源的通道,它適合于修改量較少的情況。以微信的多次發布為例,補丁大小均在 300K 以內,它相對于傳統的發布有著很大的優勢。
以 Android 用戶的升級習慣,即使是相對活躍的微信也需要 10 天以上的時間去覆蓋 50% 的用戶。使用補丁技術,我們能做到 1 天覆蓋 70% 以上。這也是基于補丁體積較小,可以直接使用移動網絡下載更新。
正因如此,補丁技術非常適合使用在灰度階段。在過去,我們需要在正式發布前保證所有嚴重的問題都已經得到修復,這通常需要我們經過三次以上的灰度過程,而且無法快速的驗證這些問題在同一批用戶的修復效果。利用熱補丁技術,我們可以快速對同一批用戶驗證修復效果,這大大縮短我們的發布流程。
若發布版本出現問題或緊急漏洞,傳統方式需要單獨灰度驗證修改,然后重新發布新的版本。利用補丁技術,我們只需要先上線小部分用戶驗證修改的效果,最后再全量上線即可。但是此種發布對線上用戶影響較大,我們需要謹慎而為。本著對用戶負責的態度,發布補丁等同于發布版本,它也應該嚴格執行完整的測試與上線流程。
總的來說,補丁技術可以降低開發成本,縮短開發周期,實現輕量而快速的升級。
三. 遠端調試
一入 Android 深似海,Android 開發的另外一個痛是機型的碎片化。我們也許都會遇到 本地不復現,日志查不出,聯系用戶不鳥你 的煩惱。所以補丁機制非常適合使用在遠端調試上。即我們需要具備只特定用戶發送補丁的能力,這對我們查找問題非常有幫助。
利用補丁技術,我們避免了騷擾用戶而默默的為用戶解決問題。當然這也需要非常嚴格的權限管理,以防惡意或隨意使用。
四. 數據統計
數據統計在微信中也占據著非常重要的位置,我們也非常希望將熱補丁與數據統計結合的更好。事實上,熱補丁無論在普通的數據統計還是 ABTest 都有著非常大的優勢。例如若我想對同一批用戶做兩種 test, 傳統方式無法讓這批用戶去安裝兩個版本。使用補丁技術,我們可以方便的對同一批用戶更換補丁版本。
在數據統計之路,如何與補丁技術結合的更好,更加精準的控制樣本人數與比例,這也是微信當前努力發展的一個方向。
五. 其他
事實上,Android 官方也使用熱補丁技術實現 Instant Run。它分為 Hot Swap、Warm Swap 與 Cold Swap 三種方式,大家可以參考英文介紹,也可以看參考文章中的翻譯稿。最新的 Instant App 應該也是采用類似的原理,但是 Google Play 是不允許下發代碼的,這個海外 App 需要注意一下。
六. 微信熱補丁技術的演進之路
在了解補丁技術可以與適合做什么之后,我們回到技術本身。由于 Dexposed 無法支持全平臺,并不適合應用到商業產品中。所以這里我們只簡單介紹 Andfix、Qzone、微信幾套方案的實現,以及它們方案面臨著的問題,大家也可以參考資料中的各大熱補丁方案分析和比較一文。
1. AndFix
AndFix 采用 native hook 的方式,這套方案直接使用 dalvik_replaceMethod 替換 class 中方法的實現。由于它并沒有整體替換 class, 而 field 在 class 中的相對地址在 class 加載時已確定,所以 AndFix 無法支持新增或者刪除 filed 的情況 (通過替換 init 與 clinit 只可以修改 field 的數值)。
也正因如此,Andfix 可以支持的補丁場景相對有限,僅僅可以使用它來修復特定問題。結合之前的發布流程,我們更希望補丁對開發者是不感知的,即他不需要清楚這個修改是對補丁版本還是正式發布版本 (事實上我們也是使用 git 分支管理 +cherry-pick 方式)。另一方面,使用 native 替換將會面臨比較復雜的兼容性問題。
相比其他方案,AndFix 的最大優點在于立即生效。事實上,AndFix 的實現與 Instant Run 的熱插拔有點類似,但是由于使用場景的限制,微信在最初期已排除使用這一方案。
2. Qzone
Qzone 方案并沒有開源,但在 github 上的 Nuwa 采用了相同的方式。這個方案使用 classloader 的方式,能實現更加友好的類替換。而且這與我們加載 Multidex 的做法相似,能基本保證穩定性與兼容性。具體原理在這里不再細說,大家可以參考 安卓 App 熱補丁動態修復技術介紹 這篇文章。
本方案為了解決 unexpected DEX problem 異常而采用插樁的方式,從而規避問題的出現。事實上,Android 系統的這些檢查規則是非常有意義的,這會導致 Qzone 方案在 Dalvik 與 Art 都會產生一些問題。
Dalvik;在 dexopt 過程,若 class verify 通過會寫入 pre-verify 標志,在經過 optimize 之后再寫入 odex 文件。這里的 optimize 主要包括 inline 以及 quick 指令優化等。
若采用插樁導致所有類都非 preverify,這導致 verify 與 optimize 操作會在加載類時觸發。這會有一定的性能損耗,微信分別采用插樁與不插樁兩種方式做過兩種測試,一是連續加載 700 個 50 行左右的類,一是統計微信整個啟動完成的耗時。
平均每個類 verify+optimize(跟類的大小有關系) 的耗時并不長,而且這個耗時每個類只有一次。但由于啟動時會加載大量的類,在這個情況影響還是比較大。
Art;Art 采用了新的方式,插樁對代碼的執行效率并沒有什么影響。但是若補丁中的類出現修改類變量或者方法,可能會導致出現內存地址錯亂的問題。為了解決這個問題我們需要將修改了變量、方法以及接口的類的父類以及調用這個類的所有類都加入到補丁包中。這可能會帶來補丁包大小的急劇增加。
這里是因為在 dex2oat 時 fast* 已經將類能確定的各個地址寫死。如果運行時補丁包的地址出現改變,原始類去調用時就會出現地址錯亂。這里說的可能不夠詳細,事實上微信當時為了查清這兩個問題,也花費了一定的時間將 Dalvik 跟 Art 的流程基本搞透。若大家對這里感興趣,后續在單獨的文章詳細論述。
總的來說,Qzone 方案好處在于開發透明,簡單,這一套方案目前的應用成功率也是最高的,但在補丁包大小與性能損耗上有一定的局限性。特別是無論我們是否真正應用補丁,都會因為插樁導致對程序運行時的性能產生影響。微信對于性能要求較高,所以我們也沒有采用這套方案。
3. 微信熱補丁方案
有沒有那么一種方案,能做到開發透明,但是卻沒有 Qzone 方案的缺陷呢?Instant Run 的冷插拔與 buck 的 exopackage 或許能給我們靈感,它們的思想都是全量替換新的 Dex。即我們完全使用了新的 Dex,那樣既不出現 Art 地址錯亂的問題,在 Dalvik 也無須插樁。當然考慮到補丁包的體積,我們不能直接將新的 Dex 放在里面。但我們可以將新舊兩個 Dex 的差異放到補丁包中,最簡單我們可以采用 BsDiff 算法。
簡單來說,在編譯時通過新舊兩個 Dex 生成差異 patch.dex。在運行時,將差異 patch.dex 重新跟原始安裝包的舊 Dex 還原為新的 Dex。這個過程可能比較耗費時間與內存,所以我們是單獨放在一個后臺進程:patch 中。為了補丁包盡量的小,微信自研了 DexDiff 算法,它深度利用 Dex 的格式來減少差異的大小。它的粒度是 Dex 格式的每一項,可以充分利用原本 Dex 的信息,而 BsDiff 的粒度是文件,AndFix/Qzone 的粒度為 class。
這塊后面我希望后面用單獨的文章來講述,這里先做一個鋪墊,大致的效果如下圖。在最極端的情況,由于利用了原本 dex 的信息完全替換一個 13M 的 Dex,我們的補丁大小也僅僅只有 6.6M。
但是這套方案并非沒有缺點,它帶來的問題有兩個:
占用 Rom 體積;這邊大約是你所修改 Dex 大小的 1.5 倍 (Dex 壓縮成 jar 的大小加上生成的 dexopt 文件大小)。
一個額外的合成過程;雖然我們單獨放在一個進程上處理,但是合成時間的長短與內存消耗也會影響最終的成功率 (與修改 Dex 大小、補丁大小相關)。
微信的熱補丁方案叫做 Tinker,也算緬懷一下 Dota 中的地精修補匠,希望能做到無限刷新。
限于篇幅,這里對 Dex、library 以及資源的更多技術細節并沒有詳細的論述,這里希望放在后面的單獨文章中。我們最后從整體比較一下這幾種方案:
若不 care 性能損耗與補丁包大小,Qzone 方案是最簡單且成功率最高的方案 (沒有單獨的合成過程)。相對 Tinker 來說,它的占用 Rom 體積也更小。另一方面,Qzone 與 Tinker 的成功率當前大約相差 3% 左右。
事實上,一個完整的框架應該也是一個容易使用的框架。Tinker 對補丁版本管理、進程管理、安全校驗等都有著很好的支持。同時我們也支持 gradle 與命名行兩種接入方式。希望在不久的將來,它可以很快的跟大家見面。
七. 微信的熱補丁應用現狀
上一章節我們簡單比較了各個熱補丁的技術方案,它們解決了如何生成與加載補丁包的問題。但一個完善的熱補丁系統不應該僅限于此,它還需要包括以下幾個方面:
網絡通道;這里要解決的問題是決定補丁以何種方式推送給哪部分的用戶。
上線與后臺管理平臺;這里主要包括熱補丁的上線管理,歷史管理以及上報分析,報警監控等;
1. 網絡通道現狀
網絡通道負責的將補丁包交付給用戶,這個包括特定用戶與全量用戶兩種情況。事實上,微信當前針對熱補丁有以下三種通道更新:
pull 通道; 在登陸 /24 小時等時機,通過 pull 方式查詢后臺是否有對應的補丁包更新,這也是我們最常用的方式;
指定版本的 push 通道; 針對版本的通道,在緊急情況下,我們可以在一個小時內向所有用戶下發補丁包更新。
指定特定用戶的 push 通道; 對特定用戶或用戶組做遠程調試。
事實上,對于大部分的應用來說,假設不實現 push 通道,CDN+pull 通道實現起來還是較為容易。
2. 上線與管理平臺現狀
上線與管理平臺主要為了快速上線,管理歷史記錄,以及監控補丁的運行情況等 (界面比較丑陋,因為我們木有美工啊)。
事實上,微信發布熱補丁是非常慎重的。它整個發布流程與升級版本是保持一致的,也必須修改版本號、經過嚴格的完整測試流程等。我們也會通過灰度的方式上線,同時監控補丁版本的各個指標。這里的為了完整的監控補丁的情況,我們做的工作有:
1 分鐘粒度的每小時 / 每天的各版本累積用戶,及時監控補丁版本的人數與活躍;
3 分鐘粒度的 Crash 統計,基準版本與補丁版本的 Crash 每小時 / 每天的兩個維度對照;
10 分鐘粒度的補丁監控信息上報。
3. 補丁成功率現狀
應用成功率 = 補丁版本人數 / 補丁發布前該版本人數
由于可能存在基準或補丁版本用戶安裝了其他版本,所以本統計結果應略為偏低,但它能現實的反應補丁的線上覆蓋情況。
使用 Qzone 方案,微信補丁在 10 天后的應用成功率大約在 98.5% 左右。使用 Tinker 大約只有 95.5% 左右,主要原因在于空間不足以及后臺進程被殺。在這里我們也在嘗試使用重試的方式以及降低合成的耗時與內存,從而提升成功率。
關于“Android 熱補丁怎么理解”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Android 熱補丁怎么理解”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注丸趣 TV 行業資訊頻道。