1. 程式人生 > >十三、設定Multidex出現java.lang.NoClassDefFoundError

十三、設定Multidex出現java.lang.NoClassDefFoundError

java.lang.NoClassDefFoundError: org.chiki.base.utils.CommonUtils
at com.***.common.Common$Config.<clinit>(Common.java:32)
at com.***.app.APPContext.initDB(APPContext.java:66)
at com.***.app.APPContext.onCreate(APPContext.java:55)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java
:1024) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4372) at android.app.ActivityThread.access$1500(ActivityThread.java:135) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop
(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5045) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) at com.android.internal.os.ZygoteInit
.main(ZygoteInit.java:595) at dalvik.system.NativeStart.main(Native Method)

一、出現場景
在Android6.0上編譯沒有問題,而在Android4.+上就出現了這種錯誤。

二、查詢問題
看到這個錯誤的時候,有點懵了,NoClassDefFoundError(沒有發現類錯誤),上面CommonUtils類是在Lib中的,如果說沒有匯入庫的話,那就有點說不過去了吧。
通過檢查build.gradle看到如下這句話:

android {
    ...
    defaultConfig {
        ...
        // 設定MultiDex可用
        multiDexEnabled true
    }
}

對此很好奇這是有什麼用的呢?查閱了相關資料,原來是做編譯class.dex分包用的。

然後我去解壓了apk檔案,發現解壓出來的檔案中包含有兩個.dex檔案:classes.dex和classes2.dex,再看java.lang.NoClassDefFoundError,結果顯而易見,方法數超限了!

而我的專案中正是缺少了下面三步中的其中一步,然後報了錯誤。

三、解決方式:
這裡可以分成四步:

1、 配置完整的build.gradle

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.0"
    // productFlavors是為了避免每次執行都把DEX重新載入一遍而設定的兩套執行配置
    productFlavors {
        dev {
            minSdkVersion 21
        }
        prod {
            minSdkVersion 14
        }
    }
    ...
    defaultConfig {
        ...
        minSdkVersion 14
        targetSdkVersion 21
        ...
        // 設定MultiDex可用
        multiDexEnabled true
        //MultiDex手動拆包
        //multiDexKeepFile file ('multiDexKeep.txt')
        //multiDexKeepProguard file('multiDexKeep.pro')
    }
    // 保證其他的lib沒有被preDex
    dexOptions {
        preDexLibraries = false
    }
}
dependencies {
    ...    
    // MultiDex的依賴
    compile 'com.android.support:multidex:1.0.1'
    ...
}

2、設定AndroidManifest.xml:引入自定義的MyApplication

 <application
        android:name=".MyApplication"
        ....>
</application>

3、設定MyApplication

public class MyApplication extends MultiDexApplication {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(base);
    }
}

4、MultiDex手動拆包:如果上面三步可以了的話,這一步就可以省了。

Android Studio 中提供了相應的手動拆包的方法(可以任選其一),就是第一步中的那兩個檔案應放置於app->build.gradle同一個目錄。
這裡寫圖片描述

  • multiDexKeepFile:手動加入要放到Main.dex中的類。
android/support/multidex/MultiDex.class
  • multiDexKeepProguard:以Proguard的方式手動加入要放到Main.dex中的類。
-keep class android.support.multidex.** {*;}

四、MultiDex的產生背景:
隨著時代的發展,現在的應用不斷的添加了很多新的功能,酷炫的效果,引入第三方庫也成了正常不過的事情,到達一定規模後就可能遇到方法數超限問題。

早期版本錯誤資訊如下:

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536

較新版本錯誤資訊如下:

trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.

其中數字65536是關鍵,Android平臺的Java虛擬機器Dalvik執行Dex程式時,使用的是short型別來索引DEX檔案中的方法。

這就意味著單個Dex檔案可被引用的方法總數被限制為64x1024, 即65536。

因此當專案足夠大包含方法數目足夠多超過了65535(包括引用的外部Lib裡面的所有方法),當執行App,就會得到超限錯誤提示。

為突破這個限制,需要使用multidex來生成多個dex檔案,其中也有分主次

Android5.0 (API level 21)之前版本支援Multidex

Android5.0之前使用Dalvik執行時執行應用程式碼,預設Dalvik限制每個apk只能有一個位元組碼classed.dex檔案。為突破這個限制,可以使用multidex support library來管理額外的dex檔案(包括程式碼)。

Android5.0及更高版本支援Multidex

Android5.0及更高版本使用支援從apk中載入多個dex檔案的ART執行時機制,在應用安裝時,載入classed(…N).dex檔案並編譯成一個.oat檔案以支援在Android裝置上執行。關於Android 5.0執行時詳見ART介紹。

Note: While using Instant Run, Android Studio automatically configures your app for multidex when your app’s minSdkVersion is set to 21 or higher. Because Instant Run only works with the debug version of your app, you still need to configure your release build for multidex to avoid the 64K limit.

如果使用Instant Run,當app的minSdkVersion大於或等於21時,Android Studio會自動配置支援multidex,但是僅debug版本有效,release版仍然需要配置multidex來突破64K限制。

看到這裡你應該知道我的場景Android6.0可以,然後4.0不行的情況了吧。因為大於5.0自動配置支援了multidex。

借鑑: