理解和解决安卓中的多指标问题

197 阅读6分钟

了解并解决安卓系统中的多指标问题

在某些情况下,如果引用方法超过65,536个,你的Android项目可能无法构建。在本教程中,我们将学习如何通过允许multidex来防止这个错误。

在Android中,源代码在编译过程中被转换为DEX(Dalvik Executable)文件。

一个DEX文件只接受64K 方法,因此如果超过了这个限制,在构建你的项目时就会出现错误。

这个限制被称为64K参考限制,因为K 代表一个Kilo ,相当于1024(2^10)。

前提条件

要继续学习本教程,读者应该熟悉以下内容。

  • 创建Android Studio项目。
  • 向Android项目添加依赖项。
  • 构建和调试Android应用程序。

为了理解什么是DEX文件,我们将简要地看一下Android的构建系统。这是一个安卓应用在转化为APK之前所经历的阶段。

构建过程包括以下几个阶段。

Android Build System

一旦你开始构建你的项目,源代码就会被编译器转换为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通过以下方式进行应用优化。

  1. 移除未使用的类、方法和字段。这有助于缩小应用程序,从而避免了64K的引用限制。
  2. 移除未使用的资源。资源包括包含在你的应用程序中的依赖性。
  3. 优化代码本身。在这种情况下,R8删除了未使用的语句,如空的if语句、循环、try-catch块,以及一些可重用的代码。
  4. 代码模糊化。在这种情况下,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库的限制,你也可以考虑缩减代码。