当您的应用及其引用的库包含的方法数超过 65536 时,您会遇到一个构建错误,指明您的应用已达到 Android 构建架构规定的引用限制:
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]: 65536
这两种错误情况会显示一个共同的数字:65536。此数字是单个 Dalvik Executable (DEX) 字节码文件内的代码可调用的引用总数。本页介绍如何通过启用称为 MultiDex 的应用配置(该配置使您的应用能够构建和读取多个 DEX 文件)越过这一限制。
关于 64K 引用限制
Android 应用 (APK) 文件包含 Dalvik Executable (DEX) 文件形式的可执行字节码文件,这些文件包含用来运行应用的已编译代码。Dalvik Executable 规范将可在单个 DEX 文件内引用的方法总数限制为 65536,其中包括 Android 框架方法、库方法以及您自己的代码中的方法。在计算机科学领域内,术语千(简称 K)表示 1024(即 2^10)。由于 65536 等于 64 X 1024,因此这一限制称为“64K 引用限制”。
Android 5.0 之前版本的 MultiDex 支持
Android 5.0(API 级别 21)之前的平台版本使用 Dalvik 运行时执行应用代码。默认情况下,Dalvik 将应用限制为每个 APK 只能使用一个 classes.dex
字节码文件。为了绕过这一限制,您可以在项目中添加 MultiDex 支持库:
dependencies {
def multidex_version = "2.0.1"
implementation 'androidx.multidex:multidex:$multidex_version'
}
如需查看此库的当前版本,请参阅版本页面中有关 MultiDex 的信息。
如果您不使用 AndroidX,请改为添加以下支持库依赖项:
dependencies {
implementation 'com.android.support:multidex:1.0.3'
}
此库会成为应用的主要 DEX 文件的一部分,然后管理对其他 DEX 文件及其所包含代码的访问。如需了解详情,请参阅下面有关如何针对 MultiDex 配置应用的部分。
Android 5.0 及更高版本的 MultiDex 支持
Android 5.0(API 级别 21)及更高版本使用名为 ART 的运行时,它本身支持从 APK 文件加载多个 DEX 文件。ART 在应用安装时执行预编译,扫描 classesN.dex
文件,并将它们编译成单个 .oat
文件,以供 Android 设备执行。因此,如果您的 minSdkVersion
为 21 或更高的值,则默认情况下会启用 MultiDex,并且您不需要 MultiDex 支持库。
如需详细了解 Android 5.0 运行时,请阅读 ART 和 Dalvik。
注意:使用 Android Studio 运行应用时,会针对您部署到的目标设备优化 build。这包括在目标设备搭载 Android 5.0 及更高版本时启用 MultiDex。由于此优化仅在使用 Android Studio 部署应用时应用,因此您可能仍需要为 MultiDex 配置发布 build,以规避 64K 限制。
规避 64K 限制
在将您的应用配置为支持使用 64K 或更多方法引用之前,您应该采取措施以减少应用代码调用的引用总数,包括由您的应用代码或包含的库定义的方法。以下策略可帮助您避免达到 DEX 引用限制:
- 检查应用的直接依赖项和传递依赖项 – 确保您在应用中使用任何庞大依赖库所带来的好处多于为应用添加大量代码所带来的弊端。一种常见的反面模式是,仅仅为了使用几个实用方法就在应用中加入非常庞大的库。减少应用代码依赖项往往能够帮助您规避 DEX 引用限制。
- 通过 R8 移除未使用的代码 – 启用代码缩减以针对发布 build 运行 R8。启用缩减可确保您交付的 APK 不含有未使用的代码。
使用这些技巧使您不必在应用中启用 MultiDex,同时还会减小 APK 的总体大小。
针对 MultiDex 配置应用
如果您的 minSdkVersion
设为 21 或更高的值,则默认情况下会启用 MultiDex,并且您不需要 MultiDex 支持库。
不过,如果您的 minSdkVersion
设为 20 或更低的值,则必须使用 MultiDex 支持库并对应用项目进行以下修改:
- 修改模块级
build.gradle
文件以启用 MultiDex,并将 MultiDex 库添加为依赖项,如下所示:
android {
defaultConfig {
...
minSdkVersion 15
targetSdkVersion 28
multiDexEnabled true
}
...
}
dependencies {
implementation 'com.android.support:multidex:1.0.3'
}
- 根据您是否替换
Application
类,执行以下某项操作:(这条我自己测试MultiDexApplication类找不到,可以不需要)
- 如果您不替换
Application
类,请修改清单文件以设置 <application>
标记中的 android:name
,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<application
android:name="android.support.multidex.MultiDexApplication" >
...
</application>
</manifest>
- 如果您替换
Application
类,请对其进行更改以扩展 MultiDexApplication
(如果可能),如下所示:
public class MyApplication extends MultiDexApplication { ... }
- 或者,如果您替换
Application
类,但无法更改基类,则可以改为替换 attachBaseContext()
方法并调用 MultiDex.install(this)
以启用 MultiDex:
public class MyApplication extends SomeOtherApplication {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
注意:在 MultiDex.install()
完成之前,不要通过反射或 JNI 执行 MultiDex.install()
或其他任何代码。MultiDex 跟踪功能不会追踪这些调用,从而导致出现 ClassNotFoundException
,或因 DEX 文件之间的类分区错误而导致验证错误。
现在,当您构建应用时,Android 构建工具会根据需要构造主要 DEX 文件 (classes.dex
) 和辅助 DEX 文件(classes2.dex
和 classes3.dex
等)。然后,构建系统会将所有 DEX 文件打包到 APK 中。
在运行时,MultiDex API 使用特殊的类加载器搜索适用于您的方法的所有 DEX 文件(而不是只在主 classes.dex
文件中搜索)。
MultiDex 支持库的局限性
MultiDex 支持库具有一些已知的局限性,将其纳入您的应用构建配置时,您应注意这些局限性并进行针对性的测试:
- 启动期间在设备的数据分区上安装 DEX 文件的过程相当复杂,如果辅助 DEX 文件较大,可能会导致应用无响应 (ANR) 错误。为避免此问题,请启用代码缩减,以尽量减小 DEX 文件的大小,并移除未使用的代码部分。
- 当搭载的版本低于 Android 5.0(API 级别 21)时,使用 MultiDex 不足以避开 linearalloc 限制(问题 78035)。此上限在 Android 4.0(API 级别 14)中有所提高,但这并未完全解决该问题。在低于 Android 4.0 的版本中,您可能会在达到 DEX 索引限制之前达到 linearalloc 限制。因此,如果您的目标 API 级别低于 14,请在这些版本的平台上进行全面测试,因为您的应用可能会在启动时或加载特定类组时出现问题。代码缩减可以减少甚至有可能消除这些问题。