随着业务逻辑越来越多,业务模块也越来越大,不可避免会遇到64K方法数的限制。最直观的表现就是编译报错:
较早版本的编译系统中,错误内容如下:
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.
64K限制的原因
Android APK文件本质上是一个压缩文件,它包含的classes.dex文件是可执行的Dalvik字节码文件,这个.dex文件中存放的就是编译后的Java代码。Dalvik可执行文件规范限制了单个.dex文件最多引用的方法数是65536个。其中包含联网Android Framework、APP引用的第三方函数库以及APP自身的方法。
使用MultiDex解决64K限制的问题
Android 5.0之前的版本
Android 5.0之前系统使用的Dalvik虚拟机默认为每个APK只生成一个classes.dex文件,为了规避单个.dex文件超过64K的问题,我们拆分成多个classes.dex文件。启动应用时先加载的classex.dex为主dex文件,其他的为从dex文件。Google推出一个名为MultiDex Support Library的函数库来避免这个问题。
Android 5.0及之后的版本
Android 5.0 之后使用ART来代替Dalvik虚拟机,ART天然支持从APK文件中加载多个.dex文件。在应用安装期间,它会执行一个预编译操作,并将多个单一的.dex文件编译成一个单一的.oat文件,应用运行时区加载这个.oat文件,而不是再去一个一个加载.dex文件。
配置MultiDex
Android Gradle 插件在 Android SDK Build Tools 21.1 及更高版本的编译工具上支持multidex作为编译配置的一部分,所以确保我们的Android SDK Build Tools tools已经更新至21.1或更高版本,然后再来配置应用的multidex部分。
第一步,修改app/build.grale文件,使项目能够使用multidex:
android {
compileSdkVersion 21
buildToolsVersion "21.1.0" // 这里必须要21.1.0之后
defaultConfig {
minSdkVersion 14
targetSdkVersion 21
...
// Enabling multidex support.
multiDexEnabled true
}
...
}
dependencies {
compile 'com.android.support:multidex:1.0.1'
}
第二步,修改AndroidManifest.xml文件,引用MultiDexApplication类:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yifeng.mdstudysamples">
<application
...
android:name="android.support.multidex.MultiDexApplication">
...
</application>
</manifest>
如果用户继承变重写了Application,可以将继承的Application换成MultiDexApplication。
public class MyApplication extends MultiDexApplication {
@Override
public void onCreate() {
super.onCreate();
}
}
或者重写attachBaseContext() 方法
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
特别注意,如果没有实现这部分代码,运行时会出现NoClassDefFoundError的错误,尤其是在依赖三方函数库时。
在开发阶段优化MultiDex的构建
multidex会加长构建应用的时间,这个必要的过程可能会拖慢你的开发进度。原因在于构建系统需要经过复杂的计算决定哪些类要包含在主.dex文件中,哪些类可以包含在从dex文件中。 为加速构建过程,我们可以在Gradle中配置productFlavors来创建两个flavor:一个是开发阶段使用的,一个是生产阶段使用的。开发阶段将minSdkVersion改为21使用ART运行时机制,这样能加快构建速度。release时改为合适的minSdkVersion,这样仅在release时费时较长。
android {
compileSdkVersion 21
buildToolsVersion "21.1.0" // 这里必须要21.1.0之后
productFlavors{
// 配置不同的falvor
dev {
minSdkVersion 21 // 开发环境的minSdkVersion
}
prod {
minSdkVersion 14 // 生产环境的minSdkVersion
}
}
...
}
dependencies {
compile 'com.android.support:multidex:1.0.1'
}
- 这里每个module对应的.dex文件不会被合并,因此避免了决定哪些类要放在主dex文件中的耗时计算。
- 只能在Android 5.0 设备上进行测试