了解并解决安卓系统中的多指标问题
在某些情况下,如果引用方法超过65,536个,你的Android项目可能无法构建。在本教程中,我们将学习如何通过允许multidex来防止这个错误。
在Android中,源代码在编译过程中被转换为DEX(Dalvik Executable)文件。
一个DEX文件只接受64K 方法,因此如果超过了这个限制,在构建你的项目时就会出现错误。
这个限制被称为64K参考限制,因为K 代表一个Kilo ,相当于1024(2^10)。
前提条件
要继续学习本教程,读者应该熟悉以下内容。
- 创建Android Studio项目。
- 向Android项目添加依赖项。
- 构建和调试Android应用程序。
为了理解什么是DEX文件,我们将简要地看一下Android的构建系统。这是一个安卓应用在转化为APK之前所经历的阶段。
构建过程包括以下几个阶段。

一旦你开始构建你的项目,源代码就会被编译器转换为DEX(Dalvik Executable)文件。这个文件包含字节码,每当应用程序启动时,就会在安卓设备上运行。
DEX文件和编译后的资源被APK打包器合并成一个APK。APK打包器对生成的APK进行签名,以便在安卓设备上创建一个可执行文件。
最后,进行代码优化,以删除最终APK文件中未使用的代码。
为什么是Multidex
当你超过64K的引用限制时,Multidex是至关重要的。multidex库将帮助你避免构建错误,特别是在创建需要众多依赖和方法的复杂应用程序时。
你会遇到的错误看起来就像下面这个。
trouble writing output:
Too many field references: 120000; max is 65536.
You may try using --multi-dex option.
为了解决这个错误,你可以优化你的代码或在你的安卓项目中启用Multidex支持。
优化你的应用程序以避免64K的引用限制
你可以通过减少引用方法的数量以及其他库(依赖关系)来避免你的Android应用中的muiltidex支持。
你的应用程序代码中的众多引用在超过64K引用限制时,会导致multidex错误。
为了避免multidex构建错误,可以考虑以下方法。
管理包含在你的项目中的依赖关系
依赖关系包含方法,只要你在代码中包含一个依赖关系,你就间接地将其他功能导入你的代码库中。
因此,你应该只包括应用程序需要的特定库。
使用R8删除未使用的代码
R8是一个在你的应用程序发布前进行优化的工具。这是Android的标准优化库,在Gradle中默认实现。
R8通过以下方式进行应用优化。
- 移除未使用的类、方法和字段。这有助于缩小应用程序,从而避免了64K的引用限制。
- 移除未使用的资源。资源包括包含在你的应用程序中的依赖性。
- 优化代码本身。在这种情况下,R8删除了未使用的语句,如空的if语句、循环、try-catch块,以及一些可重用的代码。
- 代码模糊化。在这种情况下,R8在构建版本前将类、函数和字段重命名为不可读的短名称。如果你计划将你的应用程序发布到play store,代码混淆是很重要的,因为它有助于防止逆向工程。
如何使用R8进行代码优化
在你的模块级build.gradle 文件中,进入buildTypes 块,将minifyEnabled 设置为true 。
默认情况下,minifyEnabled 被设置为false 。
buildTypes {
release {
minifyEnabled true // By default it is set to false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
一旦minifyEnabled 选项被设置为true ,它就会对你的代码进行优化和混淆。
R8还支持通过在buildTypes 块中把shrinkResources 设置为true 来缩减代码,如下图所示。
buildTypes {
release {
minifyEnabled true // By default it is set to false
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
当配置好后,上述策略可以帮助人们避免超过64K的引用限制,但如果失败了,那么你就必须启用Multidex。
Multidex支持库在API级别高于21(Android 5及以上)的情况下是默认启用的。因此,你不需要添加Multidex支持库。
如何配置一个应用程序以支持API级别低于21的multidex
在这种情况下,我们要处理的是那些minSdk 被设置为20 及以下的应用程序。
设置minSdk 是在模块级build.gradle 文件中完成的。低于21的API级别使用Dalvik运行时来执行应用程序代码。
默认情况下,Dalvik限制每个APK只能有一个classes.dex bytecode文件。你可以通过启用Multidex支持库来克服这个限制。
在你的模块的build.gradle 文件中,添加Multidex的依赖关系,如下所示。
dependencies{
def multidex_version = "2.0.1"
implementation "androidx.multidex:multidex:$multidex_version"
}
一旦这个依赖被添加到你的项目中,在build.gradle 文件中,你的应用程序可以管理对额外DEX文件的访问。
要启用Multidex,请在模块层面上编辑build.gradle 文件。
android {
compileSdk 31
defaultConfig {
...
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
multiDexEnabled true // Add this to enable Multidex
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
...
}
如果可能的话,如果你已经覆盖了 "应用程序 "类,请扩展MultidexApplication 类。
class MultidexDemoApp : MultiDexApplication(){
}
在你的清单中,如果Application 没有被覆盖,在application 标签中设置android:name ,如下图所示。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.kotlinflowzipopdemo">
<application
android:name="androidx.multidex.MultiDexApplication">
...
<activity
...
</activity>
</application>
</manifest>
如果你已经覆盖了Application 类,并且不可能改变基类,那么你可以覆盖attachBaseContext() 方法,并在其中调用MultiDex.install(this) 来启用Multidex。
class MultidexDemoApp : SomeExtendedApplicatio(){
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
MultiDex.install(this)
}
}
做完所有这些配置后,Android构建工具将在构建你的应用程序时构建一个主要的DEX文件(classes.dex) 和其他支持的DEX文件(classes1.dex, classes2.dex, ... classesN.dex) 。
然后,Android构建系统会将所有构建的DEX文件打包成一个APK。
Multidex支持库的局限性
尽管Multidex库有助于解决64K限制的构建错误,但它有以下限制。
-
如果二级DEX文件大于一级DEX文件,可能会出现应用无响应(ANR)的问题。
-
当针对低于21的API级别时,最好在这些平台上进行详尽的测试,因为你的应用程序可能难以启动或加载特定的类集。
结论
尽管multidex错误可以通过Multidex支持库来解决,但在启用multidex之前,你应该考虑使用R8来管理依赖关系并优化你的应用程序。
为了克服multidex库的限制,你也可以考虑缩减代码。