Android 使用android-support-multidex解決Dex超出方法數的限制問題,讓你的應用不再爆棚,androidmultidex

來源:互聯網
上載者:User

Android 使用android-support-multidex解決Dex超出方法數的限制問題,讓你的應用不再爆棚,androidmultidex

         如有轉載,請聲明出處: 時之沙: http://blog.csdn.net/t12x3456    (來自時之沙的csdn部落格)

         隨著應用不斷迭代,業務線的擴充,應用越來越大(比如整合了各種第三方sdk或者公用支援的jar包,項目耦合性高,重複作用的類越來越多),相信很多人都遇到過如下的錯誤:

UNEXPECTED TOP-LEVEL EXCEPTION:java.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536at com.android.dx.merge.DexMerger$6.updateIndex(DexMerger.java:501)at com.android.dx.merge.DexMerger$IdMerger.mergeSorted(DexMerger.java:282)at com.android.dx.merge.DexMerger.mergeMethodIds(DexMerger.java:490)at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:167)at com.android.dx.merge.DexMerger.merge(DexMerger.java:188)at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439)at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287)at com.android.dx.command.dexer.Main.run(Main.java:230)at com.android.dx.command.dexer.Main.main(Main.java:199)at com.android.dx.command.Main.main(Main.java:103)

       沒錯,你的應用中的Dex 檔案方法數超過了最大值65536的上限,簡單來說,應用爆棚了.

        那麼讓我們看一下為什麼會引起這種錯誤:

        在Android系統中,一個App的所有代碼都在一個Dex檔案裡面。Dex是一個類似Jar的儲存了多有Java編譯位元組碼的歸檔檔案。因為Android系統使用Dalvik虛擬機器,所以需要把使用Java Compiler編譯之後的class檔案轉換成Dalvik能夠執行的class檔案。這裡需要強調的是,DexJar一樣是一個歸檔檔案,裡面仍然是Java代碼對應的位元組碼檔案。當Android系統啟動一個應用的時候,有一步是對Dex進行最佳化,這個過程有一個專門的工具來處理,叫DexOptDexOpt的執行過程是在第一次載入Dex檔案的時候執行的。這個過程會產生一個ODEX檔案,即Optimised Dex。執行ODex的效率會比直接執行Dex檔案的效率要高很多。但是在早期的Android系統中,DexOpt有一個問題,也就是這篇文章想要說明並解決的問題。DexOpt會把每一個類的方法id檢索起來,存在一個鏈表結構裡面。但是這個鏈表的長度是用一個short類型來儲存的,導致了方法id的數目不能夠超過65536個。當一個項目足夠大的時候,顯然這個方法數的上限是不夠的。儘管在新版本的Android系統中,DexOpt修複了這個問題,但是我們仍然需要對低版本的Android系統做相容.

        目前比較常用的方法:(1) 應用外掛程式化,比如使用我正在參與開發的外掛程式化架構 : https://github.com/singwhatiwanna/dynamic-load-apk ,如果有建議或者相關的問題,歡迎到Github上積极參与. (2) 分割Dex,多工程: 把所需要的.class檔案或者是Jar檔案和一些源碼一起編譯產生一個Jar檔案。然後使用Android SDK提供的dx工具把Jar檔案轉成Dex檔案。我們可以提前對它進行ODex操作,讓它在被DexClassLoader載入的時候,跳過DexOpt的部分工作,從而加快載入的過程. 這兩種方法並不衝突,外掛程式化除瞭解決應用爆棚,還有很多其他的優點,可以看我之前的文章,不再複述.

        當然,Google看來也意識到了目前應用方法數爆棚的問題, 目前在已經在API 21中提供了通用的解決方案,那就是android-support-multidex.jar. 這個jar包最低可以支援到API 4的版本(Android L及以上版本會預設支援mutidex).

讓我們看一下如何應用android-support-multidex.jar(以下都以在Anroid studio中的使用為例):


首先可以用--multi-dex配置(build.gradle)進行解決,產生的Apk中將包含多個dex檔案,比如classes.dex, classes2.dex. 如下,需要對build.gradle進行修改:
afterEvaluate {    tasks.matching {        it.name.startsWith('dex')    }.each { dx ->        if (dx.additionalParameters == null) {            dx.additionalParameters = []        }        dx.additionalParameters += '--multi-dex' // enable multidex        // optional        // dx.additionalParameters += "--main-dex-list=$projectDir/<filename>".toString() // enable the main-dex-list    }}



但是預設的Dalvik 類載入器只會尋找classes.dex,所以需要將它們進行合并才能使得被識別

當然,現在有了android.support.multidex.jar的支援,一切都會變得非常簡單,首先我們看一下相關源碼的目錄,具體的原理分析我會在之後的文章中進行講解:
android/support/multidex/BuildConfig.classandroid/support/multidex/MultiDex$V14.classandroid/support/multidex/MultiDex$V19.classandroid/support/multidex/MultiDex$V4.classandroid/support/multidex/MultiDex.classandroid/support/multidex/MultiDexApplication.classandroid/support/multidex/MultiDexExtractor$1.classandroid/support/multidex/MultiDexExtractor.classandroid/support/multidex/ZipUtil$CentralDirectory.classandroid/support/multidex/ZipUtil.class

具體整合:
將如下配置加入工程 classPath
 
repositories {  jcenter()}dependencies {  compile 'com.google.android:multidex:0.1'}



接下來整合有兩個步驟:

一. 從sdk\extras\android\support\multidex\library\libs 目錄將android-support-multidex.jar匯入工程中

二. 如果你的工程中已經含有Application類,那麼讓它繼承android.support.multidex.MultiDexApplication類,

     如果你的Application已經繼承了其他類並且不想做改動,那麼還有另外一種使用方式,覆寫attachBaseContext()方法:



public class MyApplication extends FooApplication {    @Override    protected void attachBaseContext(Context base) {        super.attachBaseContext(base);        MultiDex.install(this);    }}


最後給出build.gradle中的完整配置:
buildscript {    repositories {        jcenter()    }    dependencies {        classpath 'com.android.tools.build:gradle:0.13.3'        classpath 'jp.leafytree.gradle:gradle-android-scala-plugin:1.1'    }}apply plugin: 'com.android.application'apply plugin: 'jp.leafytree.android-scala'repositories {    jcenter()}android {    compileSdkVersion 21    buildToolsVersion '21.0.2'    defaultConfig {        applicationId 'some.app'        minSdkVersion 19        targetSdkVersion 19        versionCode 1        versionName '1.0'    }}dependencies {    compile 'com.google.android:multidex:0.1'    compile 'com.android.support:support-v4:19.0.1'    compile 'com.google.android.gms:play-services:5.0.77'    compile 'org.scala-lang:scala-library:2.11.2'    compile 'org.scaloid:scaloid_2.11:3.4-10'}



使用MutiDex的主意事項


一. 如果你繼承了MutiDexApplication或者覆寫了Application中的attachBaseContext()方法.

Application類中邏輯的注意事項:

Application 中的靜態全域變數會比MutiDex instal()方法優先載入,所以建議避免在Application類中使用main classes.dex檔案中可能使用到的靜態變數,可以根據如下所示的方式進行修改:

@Override    public void onCreate() {        super.onCreate();        final Context mContext = this;        new Runnable() {            @Override            public void run() {                // put your logic here!                // use the mContext instead of this here            }        }.run();    }


常見問題


DexException: Library dex files are not supported in multi-dex mode,你可能會見到如下的錯誤:


Error:Execution failed for task ':app:dexDebug'.> com.android.ide.common.internal.LoggedErrorException: Failed to run command:    $ANDROID_SDK/build-tools/android-4.4W/dx --dex --num-threads=4 --multi-dex    ...  Error Code:    2  Output:    UNEXPECTED TOP-LEVEL EXCEPTION:    com.android.dex.DexException: Library dex files are not supported in multi-dex mode        at com.android.dx.command.dexer.Main.runMultiDex(Main.java:322)        at com.android.dx.command.dexer.Main.run(Main.java:228)        at com.android.dx.command.dexer.Main.main(Main.java:199)        at com.android.dx.command.Main.main(Main.java:103)


  對於dex 的--multi-dex 選項設定與先行編譯的library工程有衝突,因此如果你的應用中包含引用的lirary工程,需要將先行編譯設定為false:
android {    // ...    dexOptions {        preDexLibraries = false    }}



OutOfMemoryError: Java heap space

當運行時如果看到如下錯誤:
UNEXPECTED TOP-LEVEL ERROR:java.lang.OutOfMemoryError: Java heap space

dexOptions中有一個欄位用來增加java堆記憶體大小:
android {    // ...    dexOptions {        javaMaxHeapSize "2g"    }}            


參考相關資料:

1. MutiDex 官方文檔: https://developer.android.com/reference/android/support/multidex/MultiDex.html

2. http://blog.osom.info/2014/10/multi-dex-to-rescue-from-infamous-65536.html

    

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.