【騰訊Bugly乾貨分享】微信Tinker的一切都在這裡,包括源碼(一)

来源:http://www.cnblogs.com/bugly/archive/2016/10/08/5937567.html
-Advertisement-
Play Games

最近半年以來,Android熱補丁技術熱潮繼續爆發,各大公司相繼推出自己的開源框架。Tinker在最近也順利完成了公司的審核,並非常榮幸的成為github.com/Tencent上第一個正式公開的項目。 ...


微信Tinker的一切都在這裡,包括源碼(一)

最近半年以來,Android熱補丁技術熱潮繼續爆發,各大公司相繼推出自己的開源框架。Tinker在最近也順利完成了公司的審核,並非常榮幸的成為github.com/Tencent上第一個正式公開的項目。

回顧這半年多的歷程,這是一條跪著走完,坑坑不息之路。或許只有自己真正經歷過,深入研究過, 才會真正的明白

熱補丁不是請客吃飯

對熱補丁技術本身,還是對使用者來說都是如此。它並不簡單,也有著自己的局限性,在使用之前我們需要對它有所瞭解。我希望通過分享微信在這歷程中的思考與經驗,能幫助大家更容易的決定是否在自己的項目中使用熱補丁技術,以及選擇什麼樣方案。

熱補丁技術背景

熱補丁是什麼以及它的應用場景介紹,大家可以參考文章微信Android熱補丁實踐演進之路

在筆者看來Android熱補丁技術應該分為以下兩個流派:

  • Native,代表有阿裡的Dexposed、AndFix與騰訊的內部方案KKFix;
  • Java, 代表有Qzone的超級補丁、大眾點評的nuwa、百度金融的rocooFix, 餓了麽的amigo以及美團的robust。

Native流派與Java流派都有著自己的優缺點,它們具體差異大家可參考上文。事實上從來都沒有最好的方案,只有最適合自己的。

對於微信來說,我們希望得到一個“高可用”的補丁框架,它應該滿足以下幾個條件:

  1. 穩定性與相容性;微信需要在數億台設備上運行,即使補丁框架帶來1%的異常,也將影響到數萬用戶。保證補丁框架的穩定性與相容性是我們的第一要務;
  2. 性能;微信對性能要求也非常苛刻,首先補丁框架不能影響應用的性能,這裡基於大部分情況下用戶不會使用到補丁。其次補丁包應該儘量少,這關係到用戶流量與補丁的成功率問題;
  3. 易用性;在解決完以上兩個核心問題的前提下,我們希望補丁框架簡單易用,並且可以全面支持,甚至可以做到功能發佈級別。

在“高可用”這個大前提下,微信對當時存在的兩個方案做了大量的研究:

  1. Dexposed/AndFix;最大挑戰在於穩定性與相容性,而且native異常排查難度更高。另一方面,由於無法增加變數與類等限制,無法做到功能發佈級別;
  2. Qzone;最大挑戰在於性能,即Dalvik平臺存在插樁導致的性能損耗,Art平臺由於地址偏移問題導致補丁包可能過大的問題;

在2016年3月,微信為了追尋“高可用”這個目標,決定嘗試搭建自己的補丁框架——Tinker。Tinker框架的演繹並不是一蹴而就,它大致分為三個階段,每一階段需要解決的核心問題並不相同。而Tinker v1.0的核心問題是實現符合性能要求的Dex補丁框架。

Tinker v1.0-性能極致追求之路

為了穩定性與相容性,微信選擇了Java流派。當前最大難點在於如何突破Qzone方案的性能問題,這時通過研究Instant Run的冷插拔與buck的exopackage給了我們靈感。它們的思想都是全量替換新的Dex。

簡單來說,我們通過完全使用了新的Dex,那樣既不出現Art地址錯亂的問題,在Dalvik也無須插樁。當然考慮到補丁包的體積,我們不能直接將新的Dex放在裡面。但我們可以將新舊兩個Dex的差異放到補丁包中,這裡我們可以調研的方法有以下幾個:

  1. BsDiff;它格式無關,但對Dex效果不是特別好,而且非常不穩定。當前微信對於so與部分資源,依然使用bsdiff演算法;
  2. DexMerge;它主要問題在於合成時記憶體占用過大,一個12M的dex,峰值記憶體可能達到70多M;
  3. DexDiff;通過深入Dex格式,實現一套diff差異小,記憶體占用少以及支持增刪改的演算法。

如何選擇?在“高可用”的核心訴求下,性能問題也尤為重要。非常慶幸微信在當時那個節點堅決的選擇了自研DexDiff演算法,這過程雖然有苦有淚,但也正是有它,才有現在的Tinker。

一. DexDiff技術實踐

在不斷的深入研究Dex格式後,我們發現自己跳進了一個深坑,主要難點有以下三個:

  1. Dex格式複雜;Dex大致分為像StringID,TypeID這些Index區域以及使用Offset的Data區域。它們有大量的互相引用,一個小小的改變可能導致大量的Index與Offset變化;
  2. dex2opt與dex2oat校驗;在這兩個過程系統會做例如四位元組對齊,部分元素排序等校驗,例如StringID按照內容的Unicode排序,TypeID按照StringID排序…
  3. 低記憶體,快速;這要求我們對Dex每一塊做到一次讀寫,無法像baksmali與dexmerge那樣完全結構化。

這不僅要求我們需要研究透Dex的格式,也要把dex2opt與dex2oat的代碼全部研究透。現在回想起來,這的確是一條跪著走完的路。與研究Dalvik與Art執行一致,這是經歷一次次翻看源碼,一次次編Rom查看日誌,一次次dump記憶體結構換來的結果。

下麵以最簡單的Index區域舉例:

要想將從左邊序列更改成右邊序列,Diff演算法的核心在於如何生成最小操作序列,同時修正Index與Offset,實現增刪改的功能。

  1. Del 2;”b”元素被刪除,它對應的Index是2,為了減少補丁包體積,除了新增的元素其他一律只存Index;
  2. “c”, “d”, “e”元素自動前移,無須操作;
  3. Addf(5); 在第五個位置增加”f”這個元素。

對於Offset區,由於每個Section可能有非常多的元素,這裡會更加複雜。最後我們得到最終的操作隊列,為什麼DexDiff可以做到記憶體非常少?這是因為DexDiff演算法是每一個操作的處理,它無需一次性讀入所有的數據。DexDiff的各項數據如下:

通過DexDiff演算法的實現,我們既解決了Dalvik平臺的性能損耗問題,又解決了Art平臺補丁包過大的問題。但這套方案的缺點在於占Rom體積比較大,微信考慮到移動設備的存儲空間提升比較快,增加幾十M的Rom空間這個代價可以接受。

二. Android N的挑戰

信心滿滿上線後,卻很快收到華為反饋的一個Crash:

而且這個Crash只在Android N上出現,在當時對我們震動非常大,難道Android N不支持Java方式熱補丁了?難道這兩個月的辛苦都白費了嗎?一切想象都蒼白無力,只有繼續去源碼裡面找原因。

在之前的基礎上,這一塊的研究並沒有花太多的時間,主要是Android N的混合編譯模式導致。更多的詳細分析可參考文章Android N混合編譯與對熱補丁影響解析

三. 廠商OTA的挑戰

剛剛解決完Android N的問題,還在沉醉在自己的勝利的愉悅中。前線很快又傳來噩耗,小米反饋開發版的一些用戶在微信啟動時黑屏,甚至ANR.

當時第一反應是不可能,所有的DexOpt操作都是放到單獨的進程,為什麼只在Art平臺出現?為什麼小米開發版用戶反饋比較多?經過分析,我們發現優化後odex文件存在有效性的檢查:

  • Dalvik平臺:modtime/crc…
  • Art平臺: checksum/image_checksum/image_offset…

這就非常好理解了,因為OTA之後系統image改變了,odex文件用到image的偏移地址很可能已經錯誤。對於ClassN.dex文件,在OTA升級系統已完成重新dex2oat,而補丁是動態載入的,只能在第一次執行時同步執行。

這個耗時可能高達十幾秒,黑屏甚至ANR也是非常好理解。那為什麼只有小米用戶反饋比較多呢?這也是因為小米開發版每周都會推送系統升級的原因。

在當時那個節點上,我們重新的審視了全量合成這一思路,再次對方案原理本身產生懷疑,它在Art平臺上面帶來了以下幾個代價:

  1. OTA後黑屏問題;這裡或許可以通過lLoading界面實現,但並不是很好的方案;
  2. Rom體積問題;一個10M的Dex,在Dalvik下odex產物只有11M左右,但在Art平臺,可以達到30多M;
  3. Android N的問題;Android N在混合編譯上努力,被補丁全量合成機制所廢棄了。這是因為動態載入的Dex,依然是全量編譯。

回想起來,Qzone方案它只把需要的類打包成補丁推送,在Art平臺上可能導致補丁很大,但它肯定比全量合成10M的Dex少很多很多。在此我們提出分平臺合成的想法,即在Dalvik平臺合成全量Dex,在Art平臺合成需要的Dex

DexDiff演算法已經非常複雜,事實上要實現分平臺合成並不容易。

主要難點有以下幾個方面:

  • small dex的類收集;什麼類應該放在這個小的Dex中呢?
  • ClassN處理;對於ClassN怎麼樣處理,可能出現類從一個Dex移動到另外一個Dex?
  • 偏移二次修正; 補丁包中的操作序列如何二次修正?
  • Art.info的大小; 為了修正偏移所引入的info文件的大小?

慶幸的是,面對困難我們並沒有畏懼,最後實現了這一套方案,這也是其他全量合成方案所不能做到的:

  1. Dalvik全量合成,解決了插樁帶來的性能損耗
  2. Art平臺合成small dex,解決了全量合成方案占用Rom體積大, OTA升級以及Android N的問題
  3. 大部分情況下Art.info僅僅1-20K, 解決由於補丁包可能過大的問題

事實上,DexDiff演算法變的如此複雜,怎麼樣保證它的正確性呢?微信為此做了以下三件事情:

  1. 隨機組成Dex校驗,覆蓋大部分case;
  2. 微信200個版本的隨機Diff校驗, 覆蓋日常使用情況;
  3. Dex文件合成產物有效性校驗,即使演算法出現問題,也只是編譯不出補丁包。

每一次DexDiff演算法的更新,都需要經過以上三個Test才可以提交,這樣DexDiff的這套演算法已完成了整個閉環。

四. 其他技術挑戰

在實現過程,我們還發現其他的一些問題:

  1. Xposed等微信插件; 市面上有各種各樣的微信插件,它們在微信啟動前會提前載入微信中的類,這會導致兩個問題:

    a. Dalvik平臺:出現Class ref in pre-verified class resolved to unexpected implementation的crash;

    b. Art平臺:出現部分類使用了舊的代碼,這可能導致補丁無效,或者地址錯亂的問題。

    微信在這裡的處理方式是若crash時發現安裝了Xposed,即清除並不再應用補丁。

    1. Dex反射成功但是不生效;部分三星android-19版本存在Dex反射成功,但出現類重覆時,查找順序始終從base.apk開始。
      微信在這裡的處理方式是增加Dex反射成功校驗,具體通過在框架中埋入某個類的isPatch變數為false。在補丁時,我們自動將這個變數改為true。通過這個變數最終的數值,我們可以知道反射成功與否。

Tinker v1.0總結

一. 關於性能

通過Tinker v1,0的努力,我們解決了Qzone方案的性能問題,得到一個符合“高可用”性能要求的補丁框架。

  • 它補丁包大小非常少,通常都是10k以內;
  • 對性能幾乎沒有影響, 2%的性能影響主要原因是微信運行時校驗補丁Dex文件的md5導致(雖然文件在/data/data/目錄,微信為了更高級別的安全);
  • Art平臺通過革命性的分平臺合成,既解決了地址偏移的問題,占Rom體積與Qzone方案一致。

二. 關於成功率

也許有人會質疑微信成功率為什麼這麼低,其他方案都是99%以上。事實上,我們的成功率計算方式是:

應用成功率= 補丁版本轉化人數/基準版本安裝人數

即三天後,94.1%的基礎版本都成功升級到補丁版本,由於基礎版本人數也是持續增長,同時可能存在基準或補丁版本用戶安裝了其他版本,所以本統計結果應略為偏低,但它能現實的反應補丁的線上總體覆蓋情況。

事實上,採用Qzone方案,3天的成功率大約為96.3%,這裡還是有很多的優化空間。

三. Tinker v2.0-穩定性的探尋之路

在v1.0階段,大部分的異常都是通過廠商反饋而來,Tinker並沒有解決“高可用”下最核心的穩定性與相容性問題。我們需要建立完整的監控與補丁回退機制,監控每一個階段的異常情況。這也是Tinker v2.0的核心任務,由於邊幅問題這部分內容將放在下一篇文章。


關註Tinker,來Github給我們star吧

https://github.com/Tencent/tinker


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • //以下是一個鏈表類 function LinkedList(){ //Node表示要加入列表的項 var Node=function(element){ this.element=element; this.next=null; }; var length=0;//存儲列表項的數量 var hea ...
  • (-1)寫在前面 我用的是chrome49,如果你用的不是。可以嘗試換下瀏覽器首碼。IE在這方面的實現又特例獨行了。不想提及…,這篇是為後續做準備。 (0)快速使用 background-image:-webkit-linear-gradient(red,blue); 或者 background-i ...
  • AOS 是一個用於在頁面滾動的時候呈現元素動畫的工具庫,你可能會覺得它和 WOWJS 一樣,的確他們效果是類似的。但是AOS是 CSS3 動畫驅動的庫,當你滾動頁面的時候能讓元素動起來,當頁面滾回頂部的時候,元素能夠回到前一個狀態,如此達到迴圈動畫的效果。 ...
  • 效果圖如下: ...
  • 效果圖: ...
  • 做App測試時監測使用期間的cpu,記憶體,流量,電量等指標時,發現的企鵝很好用的工具。 備份至此,方便後期查閱 以下內容摘抄自企鵝GT官網 http://code.tencent.com/gt.html http://gt.qq.com/ 文檔 http://gt.qq.com/docs.html ...
  • 開發第一應用 可以開發屬於自己的應用,是否有點小激動?好吧!讓我們開始,首先點擊Start a new Android Studio Project創建工程:接下來需要輸入應用名稱(第一個字母要大寫)、公司域以及指定應用存放目錄,點擊Next按鈕進入下一步: 如果第一個字母不是大寫,會提示:The ...
  • 遍歷可變數組的同時刪除數組元素 獲取系統當前語言 UITableView的Group樣式下頂部空白處理 UITableView的plain樣式下,取消區頭停滯效果 獲取某個view所在的控制器 兩種方法刪除NSUserDefaults所有記錄 列印系統所有已註冊的字體名稱 獲取圖片某一點的顏色 字元 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...