使用保留规则时,请务必达到适当的精细度,以确保您在保持应用行为的同时获得益处。如需了解保留规则的良好用法以及应避免的事项,请参阅以下部分。
保留规则的良好用法
明确定义的保留规则应尽可能具体,并遵循以下模式:
对于类规范,请尽可能指定特定类、基类或带注释的类,如以下示例所示:
-keepclassmembers class com.example.MyClass { void someSpecificMethod(); }-keepclassmembers ** extends com.example.MyBaseClass { void someSpecificMethod(); }-keepclassmembers @com.example.MyAnnotation class ** { void someSpecificMethod(); }尽可能在源代码中使用注释,然后在 keep 规则中直接以这些注释为目标。这会在代码与用于保留代码的规则之间建立清晰明确的关联,从而使配置更稳健、更易于理解,并且在代码更改时不易出现中断。
例如,以下代码段演示了如何保留 MyClass 类以及其他带有
@com.example.DisplayComponent注释的类:// In the source code @com.example.DisplayComponent class MyClass { /* ... */ } // In the keep rules -keep @com.example.DisplayComponent class * {*;}我们建议您为注释命名,以便为保留代码部分的原因提供有意义的上下文。例如,对于显示组件需要保留某些部分的应用,请使用
@DisplayComponent。应尽可能声明成员规范,并且仅引用必须保留才能使应用正常运行的类部分。建议不要通过将可选成员范围定义为
{ *; }来将规则应用于整个类,除非有严格的需要。-keepclassmembers com.example.MyClass { void someSpecificMethod(); void @com.example.MyAnnotation *; }如果您使用
repackageclasses全局选项,请避免指定可选的软件包名称。这样可以缩减 DEX 文件的大小,因为重新封装的类名称中省略了软件包前缀。为通过反射访问的所有项维护 keep 规则。即使 R8 在没有明确保留规则的情况下保留了此类项,维护实际规则也至关重要,因为未来的代码更改可能会导致 R8 不再保留这些项。
如果您无法遵守这些准则,可以暂时将需要保留的代码隔离到专用软件包中,然后将保留规则应用于该软件包。不过,这并非长期的解决方案。如需了解详情,请参阅逐步采用优化措施。如需为软件包使用保留规则,请定义保留规则,如以下示例所示:
-keepclassmembers class com.example.pkg.** { *; }
需要避免的事项
保留规则语法有许多选项,但为了获得可衡量的可持续性能优势,我们建议不要使用以下选项:
- 请勿使用软件包范围的保留规则,例如
-keep class com.example.pkg.** { *; }(长期)。在配置 R8 时,可以暂时使用它们来解决问题。如需了解详情,请参阅限制优化范围。 一般来说,请谨慎使用通配符,确保仅保留所需的代码。 - 如果可能,请避免使用建议您在应用时复制并粘贴保留规则的库,尤其是软件包范围的保留规则。旨在在 Android 上顺畅运行的库应尽可能避免使用反射,并在必要时嵌入消费者保留规则。
- 避免在保留规则中使用反转运算符
!,因为您可能会无意中将规则应用于应用中的几乎每个类。
如果您无法遵循这些规则,则可能使用了大量开放式反射,应避免使用反射或避免使用使用反射的库(请参阅 Gson 案例研究)。