1. 程式人生 > >Android分包方案multidex

Android分包方案multidex

Android分包方案multidex

1.對於功能越來越複雜的app的兩大問題

  • 一:我們自己應用的方法數超過了65536 
    我們所說的方法數限制,這個方法數包括了jar包,框架,還有我們自己應用的程式碼,當我們應用的程式碼超過65536時,結果如下:

    這裡寫圖片描述

    我們看到,顯示我們方法的引用是65579.而引用數最大是65536,建議我們開啟分包方案。

    二:我們應用的方法數沒有超過65536,但是加上依賴的jar包,框架等,超過了65536(根據方案一的結果,我們應用方法數是65579,那我們刪掉200個方法,就小於65536)

    報錯如下:

    這裡寫圖片描述

  • 三:方法數並沒有超過65536,編譯也完成了,但是在android2.3以前的系統安裝的時候,會異常中止安裝。

    這個問題會發生在Android 2.2以及Android 2.3的裝置上,涉及到一個名為dexopt的程式,全稱dex optimization,即dex檔案優化程式。在優化過程中,dexopt採用一個固定大小的緩衝區(LinearAlloc)來儲存應用中所有方法的資訊,那麼之所以會出現在老版本停止安裝,是因為老版本的緩衝區的大小是5M,而在新版本中,這個緩衝區的大小是8M或者16M,在老版本中更容易超過這個限制。

    dexopt的執行過程是在第一次載入dex檔案的時候執行的。這個過程產生了一個ODEX檔案,全稱Optimised Dex。這個ODEX檔案是在安裝過程中從apk裡提取出的可執行檔案,是優化dex產生的,再把apk包裡的dex檔案刪除,這樣就做到了預先提取。如果沒有ODEX檔案,那麼系統會從apk包中提取dex然後再執行。所以優化後可以加快軟體的啟動速度,預先提取,減少對RAM的佔用。

    在早期的Android系統中,dexopt會把每一個類的方法id檢索起來,存在一個連結串列結構裡,而這個連結串列的長度是用一個short型別來儲存的,導致方法id的數目不能夠超過65536個。雖然新版本的android系統中,dexopt修復了這個問題,但是老版本的android系統的使用者市場佔有率還是佔一定比例,還是不能放棄這部分使用者的,所以我們在開發中需要對老版本的這個問題進行相容。

2.方法數越界的解決方案

  • 外掛化技術

    我們可以採用動態載入部分dex,通過將一個dex拆分成兩個或多個dex,解決方法數越界的問題。

    外掛化是一套重量級的技術方案,我們需要通過反射來呼叫外掛的類或方法,要使用一套外掛框架來配合,而且外掛化適合一些獨立的模組,相容性問題往往較多,如果只是用於解決方法數越界的話,並不是最好的方案。

  • multidex解決方案

    為了解決方法數越界的問題,Google在2014年提出了multidex的解決方案,這個方案主要是針對AndroidStudio和Gradle編譯環境的,將一個dex檔案拆成兩個或多個dex檔案。

    不過需要注意的是multidex有一個版本問題,在Android 5.0以前使用multidex需要手動引入Google提供的android-support-multidex.jar這個jar包。這個jar包我們可以在Android SDK目錄下的extras/android/support/multidex/library/libs下找到。而從Android 5.0開始,Andorid預設支援了multidex。

    所以,我們就需要注意我們的SDK版本了,如果已經支援了multidex,而我們又把android-support-multidex.jar放在了專案的libs檔案下,就會報錯。

3.在Gradle和程式碼中配置使用Multidex

  • 在Gradle中配置使用Multidex

    由於Android的Gradle外掛在Android Build Tool 21.1開始支援使用multidex,所以我們需要使用Android Build Tools 21.1及以上版本,修改app目錄下的build.gradle檔案,有兩點需要修改。

    (1)在defaultConfig中新增multiDexEnabled true這個配置項。 
    (2)在dependencies中新增multidex的依賴: 
    compile ‘com.android.support:multidex:1.0.0’

    注意buildToolsVersion要高於21.1,配置好如下: 
    這裡寫圖片描述

  • 在Gradle中配置好之後,我們還需要在程式碼中加入支援multidex的功能,有三種方案可選

    方案一:在manifest檔案中指定Application為MultiDexApplication,如下:

    這裡寫圖片描述

    方案二:寫一個Application類並繼承MultiDexApplication,並在AndroidManifest.xml的application標籤中進行註冊(在application標籤中增加name屬性,並新增自己的Application類名即可),如果不是想重寫MultiDexApplication中一些方法的話,還是方案一更方便些。如下:

    這裡寫圖片描述

    註冊如下:

    這裡寫圖片描述

    方案三:如果不想按方案二繼承,我們可以重寫Application的attachBaseContext方法,注意,這個方法比onCreate方法先執行。具體方法是建立一個新類,繼承Application,然後重寫attachBaseContext方法,並在AndroidManifest.xml的application標籤中進行註冊(與方案二註冊相同)如下:

    這裡寫圖片描述

    對於在AndroidManifest.xml中註冊,與方案二的註冊相同。

3.使用Multide分包後兩種情況的結果

我們的Demo圖如下,我們根據該圖和dex檔案反編譯的結果分析分包情況。

這裡寫圖片描述

  • 情況一:方法數沒有越界 
    我們將方法數控制在65536以內,方法數沒有越界的話,是不會分包的,解壓apk,你會發現apk裡只有一個classes.dex,如下

    這裡寫圖片描述

    將其反編譯後(不知道怎麼反編譯的可以看一下這篇博文:http://blog.csdn.net/gaozhan_csdn/article/details/51984056),結果如下:

    這裡寫圖片描述

    可以發現,我們的類都在這個主dex檔案裡,並沒有分包。

  • 情況二:方法數越界

    我們再將方法數增加到65536以上。解壓apk,結果如下: 
    這裡寫圖片描述

    對三個dex檔案反編譯一下,看看它們裡面分別都包含了什麼類。 
    classes.dex(主dex)下的類檢視: 
    這裡寫圖片描述

    classes2.dex的類檢視
    

    這裡寫圖片描述

    classes3.dex的類檢視
    

    這裡寫圖片描述

    可以發現Second類和Third類分別在classes2.dex檔案和classes3.dex檔案裡,其他類都在主dex檔案裡(classes.dex),我們用multidex的確實現了分包從而解決了方法數越界的問題。

4.使用MultiDex存在的一些問題

1.Application 中的靜態全域性變數會比MutiDex的 instal()方法優先載入,所以建議避免在Application類中使用靜態變數引用main classes.dex檔案以外dex檔案中的類。

或者這樣解決:

一些在二級Dex載入之前,可能會被呼叫到的類(比如靜態變數的類),需要放在主Dex中.否則會ClassNotFoundError. 通過修改Gradle,可以顯式的把一些類放在Main Dex中.

afterEvaluate {
    tasks.matching {
        it.name.startsWith('dex')
    }.each { dx ->
        if (dx.additionalParameters == null) {
            dx.additionalParameters = []
        }
        dx.additionalParameters += '--multi-dex'
        dx.additionalParameters += "--main-dex-list=$projectDir/<filename>".toString()
    }
}
	

注意上面是修改後的Gradle,其中是一個文字檔案的檔名,存放在和這個build.gradle指令碼同一級的檔案目錄下,而不是 專案根目錄。可以把這個文字檔案起名為multidex.keep,內容如下.實際就是把需要放在Main Dex的類羅列出來.

android/support/multidex/BuildConfig/class
android/support/multidex/MultiDex$V14/class
android/support/multidex/MultiDex$V19/class
android/support/multidex/MultiDex$V4/class
android/support/multidex/MultiDex/class
android/support/multidex/MultiDexApplication/class
android/support/multidex/MultiDexExtractor$1/class
android/support/multidex/MultiDexExtractor/class
android/support/multidex/ZipUtil$CentralDirectory/class
android/support/multidex/ZipUtil/class
	

project.afterEvaluate標籤在特定的project配置完成後執行,而gradle.projectsEvaluated在所有projects配置完成後執行。 注意afterEvaluate需要放在android{}裡,不可放外面。

但是最新的as中,會自動判斷依賴關係來分dex,比如以下application中:

public class MyApp extends MultiDexApplication {

    public static MutilTest5 mutilTest5 = new MutilTest5();

    @Override
    public void onCreate() {
        super.onCreate();
    }
}

預設情況下,本來MuitlText5要分到class2.dex裡面去,但是因為app裡靜態變數需要用到MuitlText5,如果放到class2.dex中會找不到(因為app中靜態變數初始化會在載入主dex檔案之前執行),所以會自動放到主dex檔案裡去

但是如果依靠as自動分析,在你程式碼存在反射和native的情況下,也不保證100%正確,如果不正確,還是需要自己配置哪個類放到主dex中。

 

轉自:https://www.cnblogs.com/chenxibobo/p/6076459.html

擴充套件:其實你不知道MultiDex到底有多坑https://www.cnblogs.com/tonny-li/p/7839306.html

           Android-Multidex安裝流程解析:  https://blog.csdn.net/dbs1215/article/details/79214565