1. 65536 和 64 K 引用限制
当应用及其引用的库包含的方法数超过 65536 时,构建报错:
1
2
3
4
5
6
7
8// 高版本
trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.
// 低版本
Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536Android 应用(APK)文件包含 Dalvik Executable(DEX) 文件形式的可执行字节码文件,这些文件包含用来运行应用的已编译代码
Dalvik Executable 规范将可在单个 DEX 文件内引用的方法总数限制为 65536,其中包括 Android Framework 方法、库方法以及应用程序中所有的方法
在计算机领域,术语“千”表示 1024(即 2^10),由于 65536 = 64 * 1024,因此这一限制称为“64 K 引用限制”
2. 针对 Multidex 配置应用
Google 在 2014 年提出了 Multidex 的解决方案,通过 Multidex 可以很好地解决方法数越界的问题
如果 minSdkVersion 设为 21 或更高版本,系统会默认启用 Multidex,并且不需要 Multidex 库
- Android 5.0(API 级别 21)及更高版本使用名为 ART 的运行时环境,它本身支持从 APK 文件加载多个 DEX 文件
- ART 在应用安装时执行预编译,这会扫描查找 classesN.dex 文件,并将它们编译成单个 .oat 文件,以供 Android 设备执行
如果 minSdkVersion 设为 20 或更低版本,必须使用 Multidex 库并在代码中做相应配置
gradle 文件配置:修改模块级 build.gradle 文件以启用 Multidex,并将 Multidex 库添加为依赖项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18android {
defaultConfig {
...
minSdkVersion 15
targetSdkVersion 28
// enable multidex support
multiDexEnabled true
}
...
}
dependencies {
// 老版本
compile 'com.android.support:multidex:1.0.0'
// 新版本
implementation "androidx.multidex:multidex:2.0.1"
}代码中配置:执行以下三种之一即可
修改 manifest 清单文件的
<application>
标签的android:name
属性1
2
3
4
5
6
7<application
// 老版本
android:name="android.support.multidex.MultiDexApplication"
// 新版本
android:name="androidx.multidex.MultiDexApplication" >
</application>让应用的 Application 继承 MultiDexApplication
1
2
3public class MyApplication extends MultiDexApplication {
...
}重写 Application 的
attachBaseContext()
方法,并调用MultiDex.install(this)
以启用 Multidex1
2
3
4
5
6
7public class MyApplication extends SomeOtherApplication {
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
Multidex.install(this);
}
}- 在 Application 中,
attachBaseContext()
比onCreate()
先执行 - 在
MultiDex.install()
完成之前,不要通过反射或 JNI 执行MultiDex.install()
或其他任何代码。MultiDex 跟踪功能不会追溯这些调用,从而导致出现ClassNotFoundException
,或因 DEX 文件之间的类分区错误而导致验证错误
- 在 Application 中,
构建时,Android 构建工具会根据需要构造主 DEX 文件(classes.dex)和辅 DEX 文件(classes2.dex 和 classes3.dex 等)。然后构建系统会将所有 DEX 文件打包到 APK 中
运行时,MultiDex API 会使用特殊的类加载器搜索适用程序中方法的所有的 DEX 文件(而不是只在主 classes.dex 文件中搜索)
3. Multidex 的局限性
- 启动期间在设备的数据分区上安装 DEX 文件的过程相当复杂,如果辅 DEX 文件较大,可能会导致 ANR。为避免此问题,应启用代码缩减以尽量减小 DEX 文件的大小,并移除未使用的代码部分
- 当搭载的版本低于 Android 5.0(API 21)时,使用 Multidex 不足以避开 linearalloc 限制(问题 78035)。此上限在 Android 4.0(API 14)中有所提高,但这并未完全解决该问题。在低于 Android 4.0 的版本中,可能会在达到 DEX 索引限制之前达到 linearalloc 限制。因此,如果 target API 低于 14,应该在这些版本的平台上进行全面测试,因为应用可能会在启动时或加载特定类组时出现问题。代码缩减可以减少甚至有可能消除这些问题
4. 声明主 DEX 文件中必需的类
- 如果主 DEX 文件中未提供启动期间需要的任何类(可能因为使用的库具有复杂的依赖项导致代码路径的可见性较低),则应用会崩溃并报错:
java.lang.NoClassDefFoundError
- 为了避免该报错,可以使用 multiDexKeepFile 和 multiDexKeepProguard 属性声明这些其他类,以手动方式将这些类指定为主 DEX 文件中的必需类。如果在 multiDexKeepFile 或 multiDexKeepProguard 文件中匹配了某个类,则会将该类添加到主 DEX 文件中