使用默认设置启用应用优化后,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 语言修饰符,例如public
或final
。借助
<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>;
}