添加保留规则

使用默认设置启用应用优化后,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'
            )
        }
    }
    // ...
}

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

如何编写保留规则

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

如需添加 keep 规则,请在 proguard-rules.pro 文件中添加 -keep 代码行。

保留规则语法

保持规则通常遵循以下格式:

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

例如,如需保留特定类及其所有成员,请使用以下命令:

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

如需查看更多示例,请参阅示例部分

keep 规则组件的作用如下:

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

    “保留”选项 说明

    -keep

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

    -keepclassmembers

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

    -keepnames

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

    -keepclassmembernames

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

    -keepclasseswithmembers

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

    我们建议您主要使用 -keepclassmembers,因为它可以实现最多的优化,然后在需要时再使用 -keepnames-keep 不允许进行任何优化,因此请尽量少用。

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

  • 借助 <ClassSpecification>,您可以指定 keep 规则应应用于哪个类(或父类或实现的接口)。在最简单的情况下,这是一个完全限定的类。

  • 借助 [{ OptionalMemberSpecification }],您可以过滤保留行为,使其仅适用于与特定模式匹配的类和方法。通常,在大多数 keep 规则中都建议这样做,以防止保留的文件超出预期。

Keep 规则示例

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

构造子类

此 keep 规则打包在 androidx.room:room-runtime,以保留通过反射实例化的数据库构造函数。

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

Android 框架 ObjectAnimator 中的反射

此 keep 规则打包在 androidx.vectordrawable:vectordrawable-animated,以便 ObjectAnimator 能够使用 JNI 从原生代码调用 getter 或 setter。请注意,Compose 中的较新动画系统不需要这样的保留规则,这只是规则结构的一个示例。

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

JNI 注册

此 keep 规则打包在 androidx.graphics:graphics-path,用于保留原生方法。如果您的库使用 env->RegisterNatives() 手动注册原生方法,则可能需要执行此操作。

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