Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android客戶端插件化熱修復各種方案對比和最全總結

Android客戶端插件化熱修復各種方案對比和最全總結

編輯:關於Android編程

個人態度

學習這項技術是關心技術後的本質,在項目中能不用就不用,因為本身這種做法是 Google 不推薦的,RN才是今後的發展方向。但是RN性能方面的優化也很重要,這方面沒深入了解。

在項目中引入新技術

在項目中使用新技術,哪怕是引入一個第三方庫也要小心謹慎,確定是否確實需要。但是還是要對不了解的新的東西要積極學習、積極研究,這樣在引入實際生產環境後也能快速踩坑出坑。

個人認為開發不能脫離業務,不能脫離實際場景為了技術而技術,也不能害怕引入新技術,風險和效率等優點是並存的,但是要合理的控制風險。

介紹名詞

  1. 插件化 – apk 分為宿主和插件部分,插件在需要的時候才加載進來

  2. 熱修復 – 更新的類或者插件粒度較小的時候,我們會稱之為熱修復,一般用於修復bug

  3. 熱更新 – 2016 Google 的 Android Studio 推出了Instant Run 功能 同時提出了3個名詞

“熱部署” – 方法內的簡單修改,無需重啟app和Activity。 “暖部署” – app無需重啟,但是activity需要重啟,比如資源的修改。 “冷部署” – app需要重啟,比如繼承關系的改變或方法的簽名變化等。

所以站在app開發者角度的“熱”是指在不發版的情況來實現更新,而Google提出的“熱”是指值是否需要重新啟動。 同時在開發插件化的時候也有兩種情景,一種是插件與宿主apk沒有交互,只是在用戶使用到的時候進行一次吊起,還有一種是與宿主有很多的交互。

從MulitiDex開始

當 Android 系統安裝一個應用的時候,有一步是對 Dex 進行優化,這個過程有一個專門的工具來處理,叫 DexOpt 。DexOpt 的執行過程是在第一次加載Dex文件的時候執行的。這個過程會生成一個 ODEX 文件,即 Optimised Dex。執行 ODex 的效率會比直接執行 Dex 文件的效率要高很多。

 

但是在早期的 Android 系統中,DexOpt 有一個問題,DexOpt 會把每一個類的方法 id 檢索起來,存在一個鏈表結 構裡面。但是這個鏈表的長度是用一個 short 類型來保存的,導致了方法 id 的數目不能夠超過65536個。當一個項目足夠大的時候,顯然這個方法數的上限是不夠的。盡管在新版本的 Android 系統中,DexOpt 修復了這個問題,但是我們仍然需要對低版本的 Android 系統做兼容。

 

為了解決方法數超限的問題,需要將該dex文件拆成兩個或多個,為此谷歌官方推出了 multidex 兼容包,配合 AndroidStudio 實現了一個 APK 包含多個 dex 的功能。


 

 

MulitDex 引起的問題有:

  1. 在應用安裝到手機上的時候dex文件的安裝是復雜的(complex)有可能會因為第二個dex文件太大導致ANR。

  2. 使用了mulitDex的App有可能在4.0(api level 14)以前的機器上無法啟動,因為Dalvik linearAlloc bug(Issue 22586) 。

  3. 使用了mulitDex的App在runtime期間有可能因為Dalvik linearAlloc limit (Issue 78035) Crash。該內存分配限制在 4.0版本被增大,但是5.0以下的機器上的Apps依然會存在這個限制。

  4. 主dex被dalvik虛擬機執行時候,哪些類必須在主dex文件裡面這個問題比較復雜。build tools 可以搞定這個問題。但是如果你代碼存在反射和native的調用也不保證100%正確。

對於 davilk 和 art 虛擬機 Mulitdex 的不同: 1. ART模式相比原來的Dalvik,會在安裝APK的時候,使用Android系統自帶的dex2oat工具把APK裡面的.dex文件轉化成OAT文件。

這裡說一下羅迪的快速加載 Mulitdex 方案:art虛擬機對dex優化需要很長時間,加載插件dex跳過優化實現加速。跳過會影響類加載的性能。

 

插件化實現方案分析對比

下面對實現插件化需要關注的一些點,和主流插件化框架進行了一些總結分析。

實現插件化需要解決的技術點

  1. 資源如何加載(資源沖突問題如何解決)?

  2. 代碼如何加載訪問訪問?

  3. 插件的管理後台包括的內容?

  4. 插件的增量更新問題(非必須) ?

  5. 加載插件中的so庫 (非必須)?

針對如上問題的解決方案

針對問題1

原理: > 資源id是在編譯時生成的,其生成的規則是0xPPTTNNNN,PP段,是用來標記apk的,默認情況下系統資源PP是01,應用程序的PP是07。TT段,是用來標記資源類型的,比如圖標、布局等,相同的類型TT值相同,但是同一個TT值不代表同一種資源,例如這次編譯的時候可能使用03作為layout的TT,那下次編譯的時候可能會使用06作為TT的值,具體使用那個值,實際上和當前APP使用的資源類型的個數是相關聯的。NNNN則是某種資源類型的資源id,默認從1開始,依次累加。

 

那麼我們要解決資源id問題,就可從TT的值開始入手,只要將每次編譯時的TT值固定,即可是資源id達到分組的效果,從而避免重復。例如將宿主程序的layout資源的TT固定為33,將插件程序資源的layout的TT值固定為03(也可不對插件程序的資源id做任何處理,使其使用編譯出來的原生的值), 即可解決資源id重復的問題了。

固定資源id的TT值的辦法也非常簡單,提供一份public.xml,在public.xml中指定什麼資源類型以什麼TT值開頭即可

還有一個方法是通過定制過的aapt在編譯時指定插件的PP段的值來實現分組,重寫過的aapt指定PP段來實現id分組。

  • 方案一: 將插件apk資源解壓,通過操作文件的方式來使用,這個只是理論上可行,實際使用的時候還是有很多的問題。(主要是混淆後就懵逼了)

  • 方案二: 重寫 Context 的getResource() getAsset() 之類的方法。資源沖突需要擴展 aapt 實現。

  • 方案三: 打包後執行一個腳本修改資源ID。

針對問題2

原理說明:主要就是 classloader 加載dex,代理模式就是本身宿主中有Activity,通過欺騙系統來創建Activity,欺騙系統的部分hook的有深有淺(對比DroidPlugin和Small),讓這個Activity有生命周期,而動態加載模式就是運行時動態創建並編譯一個Activity類,需要使用動態創建類的工具實現動態字節碼操作。

  • 方案一: 簡單加載模式。

  • 方案二: Activity代理模式。

  • 方案三: 動態加載模式。

針對問題3:

  • 提供插件信息查詢和下載,包括回滾到某一版本。

  • 管理使用插件的apk,可以向不同版本apk 提供不同插件。

  • MD5校檢插件的合法性。

插件化現有框架分析對比

框架名稱 出現時間 作者 實現簡介 已知存在問題   AndroidDynamicLoader 2012年7月 mmin18 不使用Activity采用Fragment實現     DynamicAPK   攜程 擴展aapt解決資源問題     android-pluginmg   houkx 動態創建Activity來實現插件化     DynamicLoadApk 2014年底 百度工程師 任玉剛 通過代理Activity來實現插件化     DroidPlugin 2015年8月 奇虎360 深度hook實現 不支持通知欄定制   Small 2015年底 林光亮 比較DroidPlugin輕量一點,腳本來解決資源問題 不支持Service插件化   ACCD 2015年4月 bunnyblue OpenAtlas 之後改為ACCD 攜程開源框架參考了這個     nuwa 2015年9月 賈吉新 通過前置相同Dex來實現熱修復     AndFix 2015年11月 阿裡 使用JNI實現的熱修復,針對Davilk和Art調用的方法不同     Dexposed   阿裡   不支持Art虛擬機  

 

注意:關於存在的問題 作者只看了部分框架的,列舉的也是可能影響作者自身業務的點,僅供參考

 

後記

差不多找了一周的資料加上看各種文章源碼寫完這個文章,很多地方的了解不是那麼深入,很多東西也是拾人牙慧,希望大家批評指正。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved