添加保留规则

当您使用默认设置启用应用优化时,R8 会执行广泛的优化,以最大程度地提高您的性能优势。R8 对代码进行了实质性修改,包括重命名、移动和删除类字段和方法。如果这导致错误,您需要通过编写保留规则来指定哪些代码部分应保持不变。

R8 可能会在以下情况下错误地移除或修改代码:

  • 反射:通过反射访问的代码,例如使用 Class.forName()Method.invoke()。R8 通常无法判断哪些类或方法会以这种方式访问。
  • 序列化:序列化和反序列化所需的类或字段在 R8 看来可能未被使用(这是另一种形式的反射)。
  • Java 本机接口 (JNI):从本机代码调用的 Java 方法。R8 不会分析本机代码以查看它可能回溯到 Java 的内容。

本页介绍如何限制 R8 优化的范围。要了解如何自定义保留哪些资源,请参阅添加资源保留规则

在哪里添加保留规则

您应该将规则添加到模块根目录下的 proguard-rules.pro 文件中(该文件可能已经存在,如果不存在则创建)。要应用文件中的规则,您必须在模块级别的 build.gradle.kts(或 build.gradle)文件中声明该文件,如下面的代码所示

Kotlin

android {
    buildTypes {
        release {
            isMinifyEnabled = true
            isShrinkResources = true

            proguardFiles(
                // Default file with default optimization rules.
                getDefaultProguardFile("proguard-android-optimize.txt"),

                // File with your custom rules.
                "proguard-rules.pro"
            )
            ...
        }
    }
    ...
}

Groovy

android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true

            proguardFiles(
                // Default file with default optimization rules.
                getDefaultProguardFile('proguard-android-optimize.txt'),

                // File with your custom rules.
                'proguard-rules.pro'
            )
            ...
        }
    }
    // ...
}

默认情况下,您的构建脚本还包含 proguard-android-optimize.txt 文件。此文件包含大多数 Android 项目所需的规则,因此您应将其保留在构建脚本中。

如何编写保留规则

在应用编译期间,R8 通过分析应用的调用图来检测应用中需要保留的代码,该调用图从清单条目(如您的 activity 或 service)开始,并跟踪每个应用和库函数调用。R8 会删除未以这种方式直接引用的代码,如果执行的代码不属于此图(例如通过反射调用的代码),则可能导致问题。通过编写您自己的保留规则,您可以告知 R8 哪些代码需要保留在应用中。

要添加保留规则,请在 proguard-rules.pro 文件中添加一行 -keep

保留规则语法

保留规则通常遵循以下格式

-<KeepOption> [OptionalModifier,...] <ClassSpecification> [{ OptionalMemberSpecification }]

例如,要保留特定类及其所有成员,请使用以下内容

-keep class com.myapp.MyClass { *; }

有关更多示例,请参阅示例部分

以下是保留规则组件的作用:

  • <KeepOption> 让您可以指定要保留的类的哪些方面

    保留选项 描述

    -keep

    保留类和 [{ OptionalMemberSpecification }] 中列出的成员。

    -keepclassmembers

    允许优化类;如果类被保留,则保留 [{ OptionalMemberSpecification }] 中列出的成员。

    -keepnames

    允许删除类和成员,但不要混淆或以其他方式修改。

    -keepclassmembernames

    允许删除类和成员,但不要混淆或以其他方式修改成员。

    -keepclasseswithmembers

    如果成员与指定模式匹配,则不删除或混淆类。

    我们建议主要使用 -keepclassmembers,因为它允许进行最多的优化,如果需要则使用 -keepnames-keep 不允许任何优化,因此请尽量谨慎使用。

  • [OptionalModifier],...] 允许您列出零个或多个类的 Java 语言修饰符,例如 publicfinal

  • <ClassSpecification> 允许您指定保留规则应应用于哪个类(或哪个超类或已实现的接口)。最简单的情况是一个完全限定的类。

  • [{ OptionalMemberSpecification }] 允许您将保留行为过滤到仅匹配特定模式的类和方法。通常建议在大多数保留规则中使用此功能,以防止保留超出预期。

保留规则示例

以下是一些设计良好的保留规则示例。

构造子类

此保留规则打包在 androidx.room:room-runtime 内部,用于保留数据库构造函数通过反射实例化

-keep class * extends androidx.room.RoomDatabase { void <init>(); }

Android Framework ObjectAnimator 的反射

此保留规则打包在 androidx.vectordrawable:vectordrawable-animated 内部,用于使 ObjectAnimator 能够通过 JNI 从本机代码调用 getter 或 setter。请注意,Compose 中较新的动画系统不需要此类保留规则,这只是规则结构的一个示例。

-keepclassmembers class androidx.vectordrawable.graphics.drawable.VectorDrawableCompat$* {
   void set*(***);
   *** get*();
}

JNI 注册

此保留规则打包在 androidx.graphics:graphics-path 内部,用于保留本机方法。如果您的库手动使用 env->RegisterNatives() 注册本机方法,则这可能是必要的。

-keepclasseswithmembers class androidx.graphics.path.** {
    native <methods>;
}