編輯:關於android開發
本文來自於騰訊bugly開發者社區,非經作者同意,請勿轉載,原文地址:http://dev.qq.com/topic/57ad7a70eaed47bb2699e68e
Dev Club 是一個交流移動開發技術,結交朋友,擴展人脈的社群,成員都是經過審核的移動開發工程師。每周都會舉行嘉賓分享,話題討論等活動。
本期,我們邀請了騰訊WXG Android開發工程師——張紹文,為大家分享《微信熱補丁Tinker的實踐演進之路》。
分享內容簡介:
Tinker 是微信官方的 Android 熱補丁解決方案,它支持動態下發代碼、So庫以及資源,讓應用能夠在不需要重新安裝的情況下實現更新。這裡大致介紹 Tinker 的實現原理,當時遇到的各種坑以及對它各個方面性能的優化工作。
內容大體框架:
下面是本期分享內容整理
hello,大家好。我是張紹文,目前在微信主要負責 Android 的性能優化以及終端質量平台相關工作。
下面開始我們今天的分享。
熱補丁技術是當前非常熱門的 Android 開發技術,其中比較出名的方案有支付寶的 AndFix以及 QZone 的超級熱補丁方案。
微信大約在2015年6月開始嘗試應用,經過研究與嘗試現有的各個方案,我們發現它們都有著自身的一些局限性。我們最終采用不同於它們的技術方案,自研微信熱補丁開源框架 Tinker。
下面我們先來講講先有框架的一些局限性。
Andfix 是阿裡推出的開源框架,它在 github 的地址是:
https://github.com/alibaba/AndFix
它的技術原理如下圖:它采用 native hook 的方式,這套方案直接使用 dalvik_replaceMethod
替換 class 中方法的實現。
它的缺點主要包括以下幾個:
現在我們來講講微信熱補丁框架 Tinker 的實現,目前在騰訊內部已開源。
它的名字來至 Dota 中的地精修補匠,我們希望發版本可以像它一樣做到無限刷新。
Tinker 的方案來源 gradle 編譯的 instant run 與 buck 編譯的 exopackage。它們的思想都是全量替換新的 Dex。即我們完全使用了新的 Dex,那樣既不出現 Art 地址錯亂的問題,在 Dalvik 也無須插樁。
但是 instant run 針對的是編譯期,它可以直接將最後生成的所有變化都直接拷到手機端。對於線上方案,這肯定是不可行的。所以當前核心問題是找到合適的,使補丁結果更小的差分算法。
微信首先 demo 中采用的是 bsdiff,它無關文件格式,但對於dex效果不是特別好,而且非常不穩定。當前微信對於 so,依然使用 bsdiff 算法。
然後我們想到 dexmerge 算法,把修改跟新增的類通過 dexmerge 方式與原來的 dex 合並,從而得到最終的完整 Dex。
經過實踐,dexmerge 的核心問題有兩個:
然後我們來看看 Tinker 的框架設計,它主要包括以下幾部分:
Q3:對於內部空間不足引起的 patch 失敗現在有什麼好的解決辦法?
對於我們的方案,空間占用有可能比較大,我們解決的方法有兩個:
你也可以在這裡采用提示用戶清理空間。Tinker 框架是可以高度定制化的。
Q4:對於替換 classloader 失敗後再用 MultiDex install 這種方案有什麼考慮?
有的,對於替換失敗的話,的確會回退到類似 Multidex install 方式的
Q5:目前微信對熱補丁技術的應用場景一般集中在哪些方面呢?除了修復緊急的 BUG,還有哪些真實場景下用過這個技術嗎?微信是如何評估是否需要通過打熱補丁的方式來處理一些問題的呢?
正如我之前的一篇文章來說,在 Android 熱補丁技術的應用比 iOS 更加容易。我們可以完全做到無感知的開發,推給用戶等。這裡面的應用場景有很多,用戶調試,版本升級,發布需求,Abtest 等等。
Q6:想問下大神,對於替換 app 中使用的第三方 jar 包,有具體實踐嗎?
抱歉,這部分還沒有實踐。原理上是沒問題的,如果第三方的 jar 包是集成到源碼,那麼編譯新包的時候已經可以帶上改變。如果第三方的 jar 包是動態加載的,也是沒有問題的。我們通過 parent classloader 的方式,查找順序也會在你們之前。
Q7:patchCoreSDK 怎麼繞過 換 classloader 後跨 dex 加載類 accesserror 的問題?有對 patchcoreSDK 做強制訪問隔離嗎?
是的,Tinker 框架分為兩部分,核心加載代碼,成為 loader 類,這裡大概有十幾個類,他們是不允許修改的。其他大部分 Tinker 的類也是可以通過補丁修改的,這裡 Tinker 框架已經做了處理,即在新合成的 Dex,我們已經刪除了 loader 相關的類,從而徹底避免了這個問題。
Q8:patch 成功後怎麼及時重啟其他進程?
為了保證各個進程的唯一性,我們有一個版本管理文件用於記錄當前補丁的版本。它分為 old 與 new 兩個字段。同時做了約定,只有 patch 進程可以修改 new 字段,只有主進程可以修改 old 字段,其他所有進程啟動時都只會加載 old 字段的補丁版本。然後主要主進程可以發起版本升級,即把 new 字段賦值給 old 字段,這個時候主進程要殺掉其他所有的進程,以保證統一性。
而及時重啟其他進程的問題,主要是在我剛才講的 result service。在結果回調中,我們如果發現補丁已經成功了,我們可以設置主進程在後台或者鎖屏時自殺,以達到最快的應用。
Q9:完全使用新的資源包是怎麼理解?舊的資源包會被替換刪除嗎?
舊的資源包是安裝的 apk,我們是不會刪掉的。我們只是反射系統的一些接口,把它替換成新的資源包
Q10:超級補丁方案,有沒有想過不采用插樁的方式,而是去 hook 檢驗的方法,就能緩解性能的問題?
事實上,有些人實現 hook preverify 標志來避免插樁。但是看過底層代碼,就知道是不可行的。我們要知道系統檢查那個標志位的真正原因,即使 hook 了 preverify 標志,在真正運行過程中,由於 quck 指令以及 vtable 的優化,依然運行時會出問題。這個問題告訴我們,做事情需要知其然也要知其所以然。
Q11:合成新的資源和 so 是怎麼加載的?
so 可以通過反射 classloader 的 lib path,但是我們並不建議這麼多,一來是兼容性問題,二來在某些機器上,多 abi 的判斷並不准確。我們更希望通過封裝代碼來支持。對於資源,我們處理是跟 dex 差不多,啟動時即反射調用。
Q12:是否有動態下發第三方的 jar 包,如何調用第三方 jar 包的方法。反射?
Tinker 框架只會合成輸入 pattern 下的 dex,而且在啟動的時候把他們加載。如果調用的問題,使用者自己決定的。
Q13:差量下發更新,合成的時候是否會有性能問題?是否支持(圖片)資源的差量下發?
合成的話,我們對於內存、GC 以及耗時都有大量的優化。即使是微信這樣體量的 app,從外部監控來看,大部分用戶都能在60秒以內完成。
Q14:需要在補丁合成加載之後才進入程序(交由用戶操作)嗎?
合成與加載是分開兩個過程,我們的原則是除非合成已經徹底完成,不然其他進程是不會去加載的。即補丁不會去影相其他進程的加載性能
Q15:代碼完全開源嗎?
對的,所有代碼都會開源,從編譯到各個模塊。
Q16:xposed 框架的那些插件,是通過反射調用替換值?那一般有啥方式保證安全性?保證 app 數據的安全性?
它們只要是反射調用微信的某些類,達到某些功能的篡改。事實上,如果在 root 下,單純的保護是比較難的。
Q17:為什麼要在補丁成功的時候加結果回調是為了啟動程序麼,但是和您剛才說的為了實時上報?
回調結果是為了給使用者一個回調,在這個回調裡面它可以做各種各樣的工作。例如我彈出升級完成的 dialog。我設置鎖屏或者程序進入後台後自殺,這可以加快補丁的應用
Q18:既然能加載 so 和資源,Tinker 能用於插件化嗎?
Tinker 當前沒有做四大組件的代理,但是 Tinker 未來絕對是具備這個能力的
Q19:merge 失敗後的補救機制是怎樣的?可以回退麼?
merge 失敗,我們會收到回調,這個時候我們不會加載的。在默認實現裡面,我們會刪除這些臨時文件。
Q20:這套框架目前是多少個人在維護呢?
Tinker當前有3個人在開發維護
Q21:請問資源是編譯到 arsc 中還是反射加載二進制流?
你的問題我不太明白,資源我們采用的是全量替換,即完全使用新的資源包
Q22:在加入 Tinker 之後,對各平台的加固適配如何?微信是否有加固?
微信沒有使用加固,但是加固應該是不影響的,只需要把接口改一下就可以了。
Android基礎入門教程——10.10 傳感器專題(1)——相關介紹 Android基礎入門教程——10.10 傳感器專題(1)—&
Android 實現 IOS相機滑動控件,androidios IOS相比於Android,動畫效果是一方面優勢,IOS相機切換時滑動的動畫很不錯,看著是有
Android開發文摘集合1,android文摘集合作者:張明雲 原標題:Android 開發中,有哪些坑需要注意? 作者github主頁:zmywly8866.gith
android studio上的基本動畫實現(第一篇),androidstudiohello,各位小伙伴們,在很多小伙伴們剛剛開始學習android的時候,常常會有一些p