Android插件化技術介紹

kfsu2006 7年前發布 | 8K 次閱讀 安卓開發 Android開發 移動開發

插件

Android插件化的目的主要有兩個,第一個是應對每個dex包65536個方法數的上限問題,第二是實現功能復雜的app的拆解,能夠按需下載和加載運行所需的模塊。插件化的實現并沒有統一的標準或方式,很多公司都有自己的一套插件化方案,而且沒有哪個公司的方案被業界普遍認可。本文將按時間軸的方式介紹幾家比較有代表性的公司的插件化方案,供大家開闊思路。至于要選取那種方案為我所用,那就只有結合自己的實際需求實踐比較得知。

非死book

非死book是進行插件化開發嘗試的鼻祖,早期非死book安裝包變大以后出現的兩個導致安裝失敗的問題:

1. Number of Java methods in our app more than 65536(64K), can‘t hold in one dex file.
 2. Large number of methods was exceeding “LinearAlloc” buffer and causing dexopt to crash

第一個錯誤是說方法數目超過最大數目64K,這是因為Android每個Dex文件的方法通過兩個字節進行索引,兩個字節能索引的最大數目就是64K。第二個錯誤是說單個dex包大小超出了Dexopt所能使用的最大緩存。Dexopt是將.dex文件進行優化,并轉化成.odex的工具,所能使用的最大緩存跟手機相關,一般為8M或16M,如果dex包太大就有可能超出Dexopt所能使用的最大緩存,導致出錯。

對此非死book采取了兩種方法來解決以上問題,總結如下:

1. 將安裝包拆分成多個dex文件,運行時通過DexClassLoader動態載入
2. 找到Delvik虛擬機的緩存設置代碼片段,替換成更大的緩存

Google

后來,Google在Android里引入這種問題一的解決方法,MultiDex方法,并將其標準化(5.0以下版本,buildtools升級到21即可使用支持包)。使用步驟:

首先修改gradle文件,在defaultConfig里面添加multiDexEnabled屬性,并修改dependencies:

android {    
    ...
    defaultConfig {        
        ...        
        minSdkVersion 14        
        targetSdkVersion 21        
        ...        
        // Enabling multidex support.        
        multiDexEnabled true    
    }    
    ...
}

dependencies {  
    compile 'com.android.support:multidex:1.0.0'
}

然后在Application的擴展類中重寫attachBaseContext函數:

public class MyApplication extends Application {
    ...
    @Override
    protected void attachBaseContext(Context base) {    
        super.attachBaseContext(base);    
        try {        
            // to resolve: java.lang.NoClassDefFoundError        
            MultiDex.install(this);    
        } catch (Exception e) {        
            e.printStackTrace();    
        }
    }
    ...
}

該方法大部分情況下可行,但是存在幾個問題:

1. 理論上該方法只解決了64K的問題,如果拆分后的包超出了LinearAlloc的大小(Dalvik linearAlloc bug),還是會觸發問題二導致crash
2. 點擊圖標啟動的時候既要對主dex文件進行dex的解壓、dexopt、加載操作,又要對輔dex文件進行dex的解壓、dexopt、加載操作,容易導致ANR錯誤,導致首次啟動初始化失敗
3. mulitDex的App有可能在4.0以前的機器上無法啟動,因為Dalvik linearAlloc bug
4. 哪些class被放在主dex文件,哪些在輔dex文件可以由build tools 搞定,但是如果你代碼存在反射和native的調用也不保證100%正確

有些技巧可以緩解存在的上述問題:

1. 盡量減少無用依賴
2. 使用Proguard進行代碼瘦身
3. 通過gradle里--set-max-idx-number=48000參數減小每個dex最多容納的方法數,寫的小一點可以多產生幾個dex文件

美團網

美團網對拆分dex包的過程進行了干預,并針對ANR錯誤進行了優化,他們要解決的三個問題以及采取的方案如下:

1. 如何控制生成多個dex包
    a. 在gradle中定義一個task,控制生成多個dex包
2. 如何控制哪些class放到主dex包,哪些放在輔dex包
    a. 把Service,Receiver和Provider以及依賴的class都放在主dex包中。Activity則進行區別對待
    b. 把首頁Activity,歡迎頁Activity等需要首先展現的Activity及依賴的class放到主dex包中,把二級,三級頁面及以來的
       class放到輔dex包中
3. 如何避免動態加載輔dex包導致的ANR錯誤
    a. 開啟一個線程異步加載輔dex包。這樣做潛在的問題是,在輔dex包加載完成前,如果啟動了輔dex包里的Activity,會導致
       ClassNotFound的錯誤。所以在所有Activity啟動前要做一個校驗,如果試圖打開Activity時輔dex包還沒有加載完成,則給個
       正在加載的提示給用戶。他們研究發現Activity是通過ActivityThread的Instrumentation來啟動的,其中跟Activity相關
       的方法包括execStartActivity,newActivity方法,所以在這兩個方法里加入了校驗過程,如果還沒加載完就給用戶展示個正
       在加載的提示

微信

微信團隊也總結了一套自己的優化方案,他們針對動態加載輔dex包進行了自己的處理,微信主要修改了動態加載輔dex包的方式:

第一次加載輔dex包時需要進行dexopt操作,這個過程比較耗時,但是如果已經加載過一次再加載就會很快了,所以微信的策略是,第一次加載的時候在sharedpreference中寫個標記,下次啟動如果發現已經有該標記,直接往下走,否則在新開一個透明的Activity,這樣這個Activity就是前臺進程,主進程就不再是前臺進程,也就不會出現ANR錯誤了。流程如下:

微信動態加載Dex文件流程

攜程網

攜程網為了實現插件化并行開發,借鑒前輩們的思路開發了一套自己的插件化方案DynamicApk,他們不僅實現了dex包的拆分和動態加載,更重要的是,他們實現了多個插件apk獨立并行開發,以便多人協作開發。

面臨的問題及解決思路:

1. 編譯期:資源和代碼的編譯
        a. 每個apk指定不同的標志位
        b. 引用宿主base.jar
    2. 運行時:資源和代碼的加載
        a. 訪問不同的資源根據標志位去不同apk加載
        b. pathList追加dex路徑

實現思路:

1. 針對插件子工程做的編譯流程改造
    2. 運行時動態加載改造

實現方式:

1. 對aapt工具的修改
    2. gradle打包腳本的實現
    3. 運行時加載代碼的實現

上面介紹的幾家公司的插件化技術可以分為兩類:多dex方案和多apk方案。這兩種方案異同總結如下:

1. 相同點
        a. 都可以把一個大工程拆分成多個組件(多個.dex文件或多個apk文件)
        b. 都可以做到使用哪個功能就按需動態加載那個組件
    2. 不同點
        多dex文件方案
            a. 需要各個小組在一個工程下開發
            b. 所有代碼合起來編譯,編譯生成一個apk包,該apk包含多個dex文件
            c. 各個dex文件只包含類文件,不包含資源文件
            d. 所有類一起編譯,編譯完了再拆分成不同的dex文件
            e. 運行時,資源都從主dex文件加載;代碼加載靠往pathList上添加dex路徑
            f. 編譯時間長
        多apk的方案
            a. 每個組有自己獨立的工程
            b. 每個組各自編譯生成自己的apk包
            c. 各個apk包都既包含資源文件,又包含類文件
            d. 各個apk包要考慮資源怎么編譯(分配不同的標志位),代碼怎么編譯(引用宿主base.jar)
            e. 運行時,資源的加載根據不同的標志位去不同apk中加載;代碼加載靠往pathList上添加dex路徑
            f. 編譯時間短

兩種方案如何選擇:

1. 多dex方案不需要額外的編碼規范,但是需要人工控制拆包的過程,成本略低
    2. 多apk方案可以節約很多編譯時間,使用該框架需要遵循一定的編碼規范,成本稍高
    3. 多dex方案成本低,編譯時間長,多apk方案成本高,編譯時間短
    4. 實踐來檢驗誰更好

其他技術方案

360的 DroidPlugin開源框架

1. 可以在不對插件apk做任何修改的情況下運行沒有安裝在手機上的apk,因為繞過了用戶授權,頗具流氓色彩,被成為安卓黑科技
    2. 代碼很亂,實用性有限

Xposed

1. 可以Hook所有app的函數或系統函數
    2. 實現系統或app層級的特效

 

來自:http://www.jianshu.com/p/5879461814ad

 

 本文由用戶 kfsu2006 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!