本文來自於騰訊bugly開發者社區,非經做者贊成,請勿轉載,原文地址:http://dev.qq.com/topic/57ecdf2d98250b4631ae034bhtml
最近半年以來,Android熱補丁技術熱潮繼續爆發,各大公司相繼推出本身的開源框架。Tinker在最近也順利完成了公司的審覈,並不是常榮幸的成爲github.com/Tencent上第一個正式公開的項目。vue
回顧這半年多的歷程,這是一條跪着走完,坑坑不息之路。或許只有本身真正經歷過,深刻研究過, 纔會真正的明白android
熱補丁不是請客吃飯git
對熱補丁技術自己,仍是對使用者來講都是如此。它並不簡單,也有着本身的侷限性,在使用以前咱們須要對它有所瞭解。我但願經過分享微信在這歷程中的思考與經驗,能幫助你們更容易的決定是否在本身的項目中使用熱補丁技術,以及選擇什麼樣方案。github
熱補丁是什麼以及它的應用場景介紹,你們能夠參考文章微信Android熱補丁實踐演進之路。算法
在筆者看來Android熱補丁技術應該分爲如下兩個流派:安全
Native流派與Java流派都有着本身的優缺點,它們具體差別你們可參考上文。事實上歷來都沒有最好的方案,只有最適合本身的。微信
對於微信來講,咱們但願獲得一個「高可用」的補丁框架,它應該知足如下幾個條件:markdown
在「高可用」這個大前提下,微信對當時存在的兩個方案作了大量的研究:app
在2016年3月,微信爲了追尋「高可用」這個目標,決定嘗試搭建本身的補丁框架——Tinker。Tinker框架的演繹並非一蹴而就,它大體分爲三個階段,每一階段須要解決的核心問題並不相同。而Tinker v1.0的核心問題是實現符合性能要求的Dex補丁框架。
爲了穩定性與兼容性,微信選擇了Java流派。當前最大難點在於如何突破Qzone方案的性能問題,這時經過研究Instant Run的冷插拔與buck的exopackage給了咱們靈感。它們的思想都是全量替換新的Dex。
簡單來講,咱們經過徹底使用了新的Dex,那樣既不出現Art地址錯亂的問題,在Dalvik也無須插樁。固然考慮到補丁包的體積,咱們不能直接將新的Dex放在裏面。但咱們能夠將新舊兩個Dex的差別放到補丁包中,這裏咱們能夠調研的方法有如下幾個:
如何選擇?在「高可用」的核心訴求下,性能問題也尤其重要。很是慶幸微信在當時那個節點堅定的選擇了自研DexDiff算法,這過程雖然有苦有淚,但也正是有它,纔有如今的Tinker。
在不斷的深刻研究Dex格式後,咱們發現本身跳進了一個深坑,主要難點有如下三個:
低內存,快速;這要求咱們對Dex每一塊作到一次讀寫,沒法像baksmali與dexmerge那樣徹底結構化。
這不只要求咱們須要研究透Dex的格式,也要把dex2opt與dex2oat的代碼所有研究透。如今回想起來,這的確是一條跪着走完的路。與研究Dalvik與Art執行一致,這是經歷一次次翻看源碼,一次次編Rom查看日誌,一次次dump內存結構換來的結果。
下面以最簡單的Index區域舉例:
要想將從左邊序列更改爲右邊序列,Diff算法的核心在於如何生成最小操做序列,同時修正Index與Offset,實現增刪改的功能。
對於Offset區,因爲每一個Section可能有很是多的元素,這裏會更加複雜。最後咱們獲得最終的操做隊列,爲何DexDiff能夠作到內存很是少?這是由於DexDiff算法是每個操做的處理,它無需一次性讀入全部的數據。DexDiff的各項數據以下:
經過DexDiff算法的實現,咱們既解決了Dalvik平臺的性能損耗問題,又解決了Art平臺補丁包過大的問題。但這套方案的缺點在於佔Rom體積比較大,微信考慮到移動設備的存儲空間提高比較快,增長几十M的Rom空間這個代價能夠接受。
信心滿滿上線後,卻很快收到華爲反饋的一個Crash:
並且這個Crash只在Android N上出現,在當時對咱們震動很是大,難道Android N不支持Java方式熱補丁了?難道這兩個月的辛苦都白費了嗎?一切想象都蒼白無力,只有繼續去源碼裏面找緣由。
在以前的基礎上,這一塊的研究並無花太多的時間,主要是Android N的混合編譯模式致使。更多的詳細分析可參考文章Android N混合編譯與對熱補丁影響解析。
剛剛解決完Android N的問題,還在沉醉在本身的勝利的愉悅中。前線很快又傳來噩耗,小米反饋開發版的一些用戶在微信啓動時黑屏,甚至ANR.
當時第一反應是不可能,全部的DexOpt操做都是放到單獨的進程,爲何只在Art平臺出現?爲何小米開發版用戶反饋比較多?通過分析,咱們發現優化後odex文件存在有效性的檢查:
這就很是好理解了,由於OTA以後系統image改變了,odex文件用到image的偏移地址極可能已經錯誤。對於ClassN.dex文件,在OTA升級系統已完成從新dex2oat,而補丁是動態加載的,只能在第一次執行時同步執行。
這個耗時可能高達十幾秒,黑屏甚至ANR也是很是好理解。那爲何只有小米用戶反饋比較多呢?這也是由於小米開發版每週都會推送系統升級的緣由。
在當時那個節點上,咱們從新的審視了全量合成這一思路,再次對方案原理自己產生懷疑,它在Art平臺上面帶來了如下幾個代價:
回想起來,Qzone方案它只把須要的類打包成補丁推送,在Art平臺上可能致使補丁很大,但它確定比全量合成10M的Dex少不少不少。在此咱們提出分平臺合成的想法,即在Dalvik平臺合成全量Dex,在Art平臺合成須要的Dex
DexDiff算法已經很是複雜,事實上要實現分平臺合成並不容易。
主要難點有如下幾個方面:
慶幸的是,面對困難咱們並無畏懼,最後實現了這一套方案,這也是其餘全量合成方案所不能作到的:
事實上,DexDiff算法變的如此複雜,怎麼樣保證它的正確性呢?微信爲此作了如下三件事情:
每一次DexDiff算法的更新,都須要通過以上三個Test才能夠提交,這樣DexDiff的這套算法已完成了整個閉環。
在實現過程,咱們還發現其餘的一些問題:
Xposed等微信插件; 市面上有各類各樣的微信插件,它們在微信啓動前會提早加載微信中的類,這會致使兩個問題:
a. Dalvik平臺:出現Class ref in pre-verified class resolved to unexpected implementation的crash;
b. Art平臺:出現部分類使用了舊的代碼,這可能致使補丁無效,或者地址錯亂的問題。
微信在這裏的處理方式是若crash時發現安裝了Xposed,即清除並再也不應用補丁。
經過Tinker v1,0的努力,咱們解決了Qzone方案的性能問題,獲得一個符合「高可用」性能要求的補丁框架。
也許有人會質疑微信成功率爲何這麼低,其餘方案都是99%以上。事實上,咱們的成功率計算方式是:
應用成功率= 補丁版本轉化人數/基準版本安裝人數
即三天後,94.1%的基礎版本都成功升級到補丁版本,因爲基礎版本人數也是持續增加,同時可能存在基準或補丁版本用戶安裝了其餘版本,因此本統計結果應略爲偏低,但它能現實的反應補丁的線上整體覆蓋狀況。
事實上,採用Qzone方案,3天的成功率大約爲96.3%,這裏仍是有不少的優化空間。
在v1.0階段,大部分的異常都是經過廠商反饋而來,Tinker並無解決「高可用」下最核心的穩定性與兼容性問題。咱們須要創建完整的監控與補丁回退機制,監控每個階段的異常狀況。這也是Tinker v2.0的核心任務,因爲邊幅問題這部份內容將放在下一篇文章。
關注Tinker,來Github給咱們star吧
更多精彩內容歡迎關注bugly的微信公衆帳號:
騰訊 Bugly是一款專爲移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的狀況以及解決方案。智能合併功能幫助開發同窗把天天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精準定位功能幫助開發同窗定位到出問題的代碼行,實時上報能夠在發佈後快速的瞭解應用的質量狀況,適配最新的 iOS, Android 官方操做系統,鵝廠的工程師都在使用,快來加入咱們吧!