Android单个dex文件所能包含的最大方法数是65536,这个包括Android FrameWork、依赖jar包以及应用本身的所有方法。当应用的方法数达到65536之后,编译出现问题。
UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536
at 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)
还有一种情况方法数没有达到65536,编译器正常编译完成,但是在低应用版本手机安装时也会报错。程序在安装时会优化dex文件,在优化的过程中会有一个固定的大小的缓冲区来存储应用中的所有方法的信息,这个缓冲区是LinearAlloc。LinearAlloc在新版本的系统中是8m或者16m,在android2.2、2.3只有5m.当安装的apk方法数较多时,尽管没有达到65536的上限,但是它的存储空间可能超出5m,进而导致安装失败。
如何解决这个问题。
首先要删除无用的代码合第三方库,但不一定会起作用。有人提到采用插件化来动态加载部分dex,悲哀啊dex拆分成多个dex,或者多个dex,这种方式一定程度上解决了方法数的问题,但是动态加载apk的技术是比较重量级的技术方案,而且兼容性也有很多问题,不推荐。
2014年Google提出了multidex解决方案。在android5.0之前,需要导入androdi-support-multidex.jar包,5.0之后默认支持了。Multidex方案主要针对的是Android Studio 和Gradle编译环境。
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "com.custom.myapplication"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
//enable multidex support
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.2.0'
testCompile ‘junit:junit:4.12'
//当前版本低于5.0需要单独导入androdi-support-multidex.jar,这个版本不需要。
}
经过上面的过程还需要在代码中添加支持multidex功能,有三种方案都比较简单。
<application
android:allowBackup="true"
android:name="android.support.multidex.MultiDexApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme=“@style/AppTheme">
……
</application>
2.让应用的Application继承MultiDexApplication
public class MainApplication extends MultiDexApplication {
......
}
同时要把Manifest的application的名字改为MainApplication
<application
android:allowBackup="true"
android:name=".MainApplication"
android:icon=“@mipmap/ic_launcher"
……
</application>
3.不想Application继承MultiDexApplication,可以重写Application的attachBaseContext方法,该方法会比Application的onCreate方法先执行。
public class MainApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}