【騰訊bugly乾貨分享】微信Android熱補丁實踐演進之路

本文來自於騰訊bugly開發者社區,非經做者贊成,請勿轉載,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1264&extra=page%3D1php

繼插件化後,熱補丁技術在2015年開始爆發,目前已是很是熱門的Android開發技術。其中比較著名的有淘寶的Dexposed、支付寶的AndFix以及QZone的超級熱補丁方案。微信對熱補丁技術的研究並不算早,大約開始於2015年6月。通過研究與嘗試現有的各個方案,咱們發現它們都有着自身的一些侷限性。微信最終採用不一樣於它們的技術方案,走出了本身的實踐演進之路。html

另一方面,技術應當只是熱補丁方案中的一環。隨着對熱補丁的屢次嘗試與應用,微信創建起自身的流程規範,同時也不斷的嘗試拓展它的應用場景。經過本文,我但願你們不只可以全面的瞭解各項熱補丁技術的優缺點,同時也能對它的應用場景有着更加全面的認識。在此基礎上,你們或許能更容易的決定是否在本身的項目中使用熱補丁技術,以及應當如何使用它。vue

爲何須要熱補丁

熱補丁:讓應用可以在無需從新安裝的狀況實現更新,幫助應用快速創建動態修復能力。git

從上面的定義來看,熱補丁節省Android大量應用市場發佈的時間。同時用戶也無需從新安裝,只要上線就能無感知的更新。看起來很美好,這是否能夠意味咱們能夠儘可能使用補丁來代替發佈呢?事實上,熱補丁技術當前依然存在它的侷限性,主要表如今如下幾點:github

  1. 補丁只能針對單一客戶端版本,隨着版本差別變大補丁體積也會增大;
  2. 補丁不能支持全部的修改,例如AndroidManifest;
  3. 補丁不管對代碼仍是資源的更新成功率都沒法達到100%。算法

    既然補丁技術沒法徹底代替升級,那它適合使用在哪些場景呢?安全

    一. 輕量而快速的升級

    熱補丁技術也能夠理解爲一個動態修改代碼與資源的通道,它適合於修改量較少的狀況。以微信的屢次發佈爲例,補丁大小均在300K之內,它相對於傳統的發佈有着很大的優點。微信

    以Android用戶的升級習慣,即便是相對活躍的微信也須要10天以上的時間去覆蓋50%的用戶。使用補丁技術,咱們能作到1天覆蓋70%以上。這也是基於補丁體積較小,能夠直接使用移動網絡下載更新。markdown

    正因如此,補丁技術很是適合使用在灰度階段。在過去,咱們須要在正式發佈前保證全部嚴重的問題都已經獲得修復,這一般須要咱們通過三次以上的灰度過程,並且沒法快速的驗證這些問題在同一批用戶的修復效果。利用熱補丁技術,咱們能夠快速對同一批用戶驗證修復效果,這大大縮短了咱們的發佈流程。網絡

    若發佈版本出現問題或緊急漏洞,傳統方式須要單獨灰度驗證修改,而後從新發布新的版本。利用補丁技術,咱們只須要先上線小部分用戶驗證修改的效果,最後再全量上線便可。可是此種發佈對線上用戶影響較大, 咱們須要謹慎而爲。本着對用戶負責的態度,發佈補丁等同於發佈版本,它也應該嚴格執行完整的測試與上線流程。

    總的來講,補丁技術能夠下降開發成本,縮短開發週期,實現輕量而快速的升級。

    二. 遠端調試

    一入Android深似海,Android開發的另一個痛是機型的碎片化。咱們也許都會遇到」本地不復現」,」日誌查不出」,」聯繫用戶不鳥你」的煩惱。因此補丁機制很是適合使用在遠端調試上。即咱們須要具有隻特定用戶發送補丁的能力,這對咱們查找問題很是有幫助。

    利用補丁技術,咱們避免了騷擾用戶而默默的爲用戶解決問題。固然這也須要很是嚴格的權限管理,以防惡意或隨意使用。

    三. 數據統計

    數據統計在微信中也佔據着很是重要的位置,咱們也很是但願將熱補丁與數據統計結合的更好。事實上,熱補丁不管在普通的數據統計仍是ABTest都有着很是大的優點。例如若我想對同一批用戶作兩種test, 傳統方式沒法讓這批用戶去安裝兩個版本。使用補丁技術,咱們能夠方便的對同一批用戶不停的更換補丁。

    在數據統計之路,如何與補丁技術結合的更好,更加精準的控制樣本人數與比例,這也是微信當前努力發展的一個方向。

    四. 其餘

    事實上,Android官方也使用熱補丁技術實現Instant Run。它分爲Hot Swap、Warm Swap與Cold Swap三種方式,你們能夠參考英文介紹,也能夠看參考文章中的翻譯稿。最新的Instant App應該也是採用相似的原理,可是Google Play是不容許下發代碼的,這個海外App須要注意一下。

    微信熱補丁技術的演進之路

    在瞭解補丁技術能夠與適合作什麼以後,咱們回到技術自己。因爲Dexposed沒法支持全平臺,並不適合應用到商業產品中。因此這裏咱們只簡單介紹Andfix、QZone、微信幾套方案的實現,以及它們方案面臨着的問題,你們也能夠參考資料中的各大熱補丁方案分析和比較一文。

    一. AndFix

    AndFix採用native hook的方式,這套方案直接使用dalvik_replaceMethod替換class中方法的實現。因爲它並無總體替換class, 而field在class中的相對地址在class加載時已肯定,因此AndFix沒法支持新增或者刪除filed的狀況(經過替換initclinit只能夠修改field的數值)。

    也正因如此,Andfix能夠支持的補丁場景相對有限,僅僅可使用它來修復特定問題。結合以前的發佈流程,咱們更但願補丁對開發者是不感知的,即他不須要清楚這個修改是對補丁版本仍是正式發佈版本(事實上咱們也是使用git分支管理+cherry-pick方式)。另外一方面,使用native替換將會面臨比較複雜的兼容性問題。

    相比其餘方案,AndFix的最大優勢在於當即生效。事實上,AndFix的實現與Instant Run的熱插拔有點相似,可是因爲使用場景的限制,微信在最初期已排除使用這一方案。

    二. QZone

    QZone方案並無開源,但在github上的Nuwa採用了相同的方式。這個方案使用classloader的方式,能實現更加友好的類替換。並且這與咱們加載Multidex的作法類似,能基本保證穩定性與兼容性。具體原理在這裏再也不細說,你們能夠參考這篇文章

    本方案爲了解決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方案好處在於開發透明,簡單,這一套方案目前的應用成功率也是最高的,但在補丁包大小與性能損耗上有必定的侷限性。特別是不管咱們是否真正應用補丁,都會由於插樁致使對程序運行時的性能產生影響。微信對於性能要求較高,因此咱們也沒有采用這套方案。

    三. 微信熱補丁方案

    有沒有那麼一種方案,能作到開發透明,可是卻沒有QZone方案的缺陷呢?Instant Run的冷插拔與buck的exopackage或許能給咱們靈感,它們的思想都是全量替換新的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中的地精修補匠,但願能作到無限刷新。

    限於篇幅,這裏對Dex、library以及資源的更多技術細節並無詳細的論述,這裏但願放在後面的單獨文章中。咱們最後從總體比較一下這幾種方案:

    若不care性能損耗與補丁包大小,QZone方案是最簡單且成功率最高的方案(沒有單獨的合成過程)。相對Tinker來講,它的佔用Rom體積也更小。另外一方面,QZone與Tinker的成功率大約相差3%左右。

    事實上,一個完整的框架應該也是一個容易使用的框架。Tinker對補丁版本管理、進程管理、安全校驗等都有着很好的支持。同時咱們也支持gradle與命名行兩種接入方式。但願在不久的未來,它能夠很快的跟你們見面。

    微信的熱補丁應用現狀

    上一章節咱們簡單比較了各個熱補丁的技術方案,它們解決了如何生成與加載補丁包的問題。但一個完善的熱補丁系統不該該僅限於此,它還須要包括如下幾個方面:

  • 網絡通道;這裏要解決的問題是決定補丁以何種方式推送給哪部分的用戶。
  • 上線與後臺管理平臺;這裏主要包括熱補丁的上線管理,歷史管理以及上報分析,報警監控等;

    一. 網絡通道現狀

    網絡通道負責的將補丁包交付給用戶,這個包括特定用戶與全量用戶兩種狀況。事實上,微信當前針對熱補丁有如下三種通道更新:

  • pull通道; 在登錄/24小時等時機,經過pull方式查詢後臺是否有對應的補丁包更新,這也是咱們最經常使用的方式;

  • 指定版本的push通道; 針對版本的通道,在緊急狀況下,咱們能夠在一個小時內向全部用戶下發補丁包更新。
  • 指定特定用戶的push通道;對特定用戶或用戶組作遠程調試。

    事實上,對於大部分的應用來講,假設不實現push通道,CDN+pull通道實現起來仍是較爲容易。

    二. 上線與管理平臺現狀

    上線與管理平臺主要爲了快速上線,管理歷史記錄,以及監控補丁的運行狀況等。

    事實上,微信發佈熱補丁是很是慎重的。它整個發佈流程與升級版本是保持一致的,也必須修改版本號、通過嚴格的完整測試流程等。咱們也會經過灰度的方式上線,同時監控補丁版本的各個指標。這裏的爲了完整的監控補丁的狀況,咱們作的工做有:

  • 1分鐘粒度的每小時/天天的各版本累積用戶,及時監控補丁版本的人數與活躍;

  • 3分鐘粒度的Crash統計,基準版本與補丁版本的Crash每小時/天天的兩個維度對照;
  • 10分鐘粒度的補丁監控信息上報。

    三. 補丁成功率現狀

    應用成功率= 補丁版本人數/補丁發佈前該版本人數
    因爲可能存在基準或補丁版本用戶安裝了其餘版本,因此本統計結果應略爲偏低,但它能現實的反應補丁的線上覆蓋狀況。

    使用Qzone方案,微信補丁在10天后的應用成功率大約在98.5%左右。使用Tinker大約只有95.5%左右,主要緣由在於空間不足以及後臺進程被殺。在這裏咱們也在嘗試使用重試的方式以及下降合成的耗時與內存,從而提高成功率。

    熱補丁技術發展的很快,Android推出的Instant App也使人期待。可是在國內,彷佛咱們仍是期望本身更靠譜一點。每個的應用的需求都不太一致,這裏大體講了一些微信的實踐經驗,但願對你們有幫助。

    將來工做

    隨着微信部門內從「單APP」向「多APP」演進,微信也正在邁入開源化的開發實踐。咱們但願將各個功能組件化,從而作能夠到快速複製與應用。微信的熱補丁框架「Tinker」當前也在經歷從微信分離,又合入到微信的過程。但願在不久的未來,咱們也能夠將「Tinker」以及微信中一些其餘的組件開源出去。

    咱們也但願能夠找一個App做爲內測,給咱們提供寶貴的意見。若對微信的Tinker方案感興趣的用戶,能夠單獨發消息或在文章末留言。註明姓名、所在公司以及負責的App,咱們但願挑選部分產品做爲內測。

    參考文章

  1. Dexposed github (https://github.com/alibaba/dexposed)
  2. AndFix github (https://github.com/alibaba/AndFix)
  3. Nuwa github (https://github.com/jasonross/Nuwa)
  4. QZone實現原理解析 (https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400118620&idx=1&sn=b4fdd5055731290eef12ad0d17f39d4a)
  5. Instant Run英文原文 (https://medium.com/google-developers/instant-run-how-does-it-work-294a1633367f#.c088qhdxu)
  6. Instant Run工做原理及用法中文翻譯稿 (http://www.jianshu.com/p/2e23ba9ff14b)
  7. Buck exopackage 介紹 (https://buckbuild.com/article/exopackage.html)
  8. 各大熱補丁方案分析和比較 (http://blog.zhaiyifan.cn/2015/11/20/HotPatchCompare/)

    更多精彩內容歡迎關注bugly的微信公衆帳號:

    騰訊 Bugly是一款專爲移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的狀況以及解決方案。智能合併功能幫助開發同窗把天天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精準定位功能幫助開發同窗定位到出問題的代碼行,實時上報能夠在發佈後快速的瞭解應用的質量狀況,適配最新的 iOS, Android 官方操做系統,鵝廠的工程師都在使用,快來加入咱們吧!

相關文章
相關標籤/搜索