(转) The Unofficial R8 Document
This is unofficial documentation for R8, Google’s code shrinker for Android™.
Google intends R8 to be a drop-in replacement for ProGuard, and has provided documentation in the Android Studio User Guide to help you get started with it.
However, they rely on the ProGuard Manual for detailed documentation, even though there are substantial differences between R8 and ProGuard.
This documentation is meant to supplement the Android Studio User Guide and the ProGuard Manual to fill that gap.
This documentation assumes that you are using the standard Gradle™ build process of an Android application or library with version 3.4 or later of the Android Gradle Plugin.
It is not suitable if you are using R8 directly in a custom build process.
Note: Known issues reflected in this document were last tested on R8 v1.6.67 using Android Gradle Plugin v3.6.1.
The Android Studio documentation and the R8 ruleset itself use the term “obfuscation” in a way that isn’t very precise.
As we make an Android Obfuscator, we think it’s important to understand the distinction between Obfuscation, Renaming, Shrinking, and Minification.
minifyEnabled
, is sometimes used to describe the combination of Shrinking and Renaming for the purpose of reducing the size of an application or library.In addition to Renaming and Code Shrinking, R8 also performs Optimization, which rewrites code to improve its performance and further reduce its size.
The Android Gradle Plugin also performs Resource Shrinking, which reduces the size of resources in a similar manner to the way that Code Shrinking reduces the size of applications or libraries.
This is not a feature of R8 itself, but they are related processes; the Android Gradle Plugin requires that you enable a code shrinker to shrink resources.
Rule | Description |
---|---|
-allowaccessmodification |
Allows R8 to change access modifiers, enabling additional optimizations and additional reorganizations to packages in which classes are contained. (ProGuard docs) |
-assumenosideeffects <class-spec> |
Informs R8 it can safely remove calls to the specified method(s) during optimization. If the method returns a value that appears to be used, the call may not be removed. Note that this rule is ignored if -dontoptimize is also configured. (ProGuard docs) |
-dontobfuscate |
Do not apply renaming, regardless of other configuration. (ProGuard docs) |
-dontoptimize |
Do not optimize the code, regardless of other configuration. This is part of the default configuration. (ProGuard docs) |
-dontshrink |
Do not remove any classes, methods, or fields, regardless of other configuration. (ProGuard docs) |
-include <filename> |
Include configuration from file with filename filename . (ProGuard docs) |
-keepattributes [<filter>] |
Allows you to specify supported Java™ attributes for R8 to retain in the code. Unlike ProGuard, R8 does not respect rules regarding Synthetic , Deprecated , or MethodParameters and will remove these attributes regardless of what is configured in -keepattributes . Also, for class version 50 (Java 6), R8 will keep a StackMapTable attribute only if StackMapTable is covered by -keepattributes ; it is always kept for later class versions. (ProGuard docs) (See issue) |
-printconfiguration [<file>] |
Outputs the used configuration rules to the specified file, or to stdout if there is no file specified. Note that if you specify a file, every build of a variant using this rule will overwrite that file. (ProGuard docs) |
-printseeds [<filename>] |
Outputs a list of the classes, methods, and fields which match the keep rules to the specified file, or to stdout if there is no file specified. Note that if you specify a file, every build of a variant using this rule will overwrite that file. Note that unlike ProGuard, R8 will not automatically output a build/outputs/mapping[/{flavorName}]/{buildType}/seeds.txt file. (ProGuard docs) |
-printusage [<filename>] |
Outputs a list of the classes, methods, and fields which were removed during shrinking to the specified file, or to stdout if there is no file specified. Note that if you specify a file, every build of a variant using this rule will overwrite that file. Note that unlike ProGuard, R8 will not automatically output a build/outputs/mapping[/{flavorName}]/{buildType}/usage.txt file. (ProGuard docs) |
Application of shrinking and renaming is configured by using the -keep*
rules.
These rules are configured by proving a class specification and optional modifiers.
Rule (and Arguments) | Description |
---|---|
-keep[,<modifier>[...]] <class-spec> |
Exclude matching classes, and matching members if specified, from shrinking, optimization, and renaming. Shrinking exclusion on the class means that members will not be removed but does not prevent members from being renamed. Specifying members will prevent them from being renamed if present. (ProGuard docs) |
-keepclassmembers[,<modifier>[...]] <class-spec> |
Exclude matching members in matching classes from shrinking, optimization, and renaming. (ProGuard docs) |
-keepclasseswithmembers[,<modifier>[...]] <class-spec> |
Exclude matching classes and matching members from shrinking, optimization, and renaming if the corresponding class has all of the specified members. (ProGuard docs) |
-keepnames[,<modifier>[...]] <class-spec> |
Prevent matching classes, and matching members if specified, from being renamed. (ProGuard docs) |
-keepclassmembernames[,<modifier>[...]] <class-spec> |
Prevent any matching members from being renamed in matching classes. (ProGuard docs) |
-keepclasseswithmembernames[,<modifier>[...]] <class-spec> |
Prevent matching classes and matching members from being renamed if the corresponding class contains all of the specified members. This does not prevent matching members from being removed by shrinking (ProGuard would also prevent the specified members from being removed). (ProGuard docs) |
-whyareyoukeeping <class-spec> |
Log details about why particular classes and members were maintained in the output. (ProGuard docs) |
-if <class-spec> <one-keep-rule> |
Conditionally apply one keep rule. If class members are specified, the class and all specified members must match. Otherwise, only the class need match. Class specification in the keep rule can contain back references to wildcards in the -if class specification. (ProGuard docs) |
Keep rule modifiers:
Modifier | Effect |
---|---|
allowshrinking |
Allow the target(s) of the rule to be removed by shrinking. (ProGuard docs) |
allowoptimization |
Allow the target(s) of the rule to be optimized. (ProGuard docs) |
allowobfuscation |
Allow the target(s) of the rule to be renamed. Adding this modifier to one of the -keep*names rules causes that rule to have no effect. (ProGuard docs) |
includedescriptorclasses |
Prevent specified field types, method return types, and method parameter types from being renamed. This preserves field and method signatures (post type-erasure, e.g. this does not preserve generic types). (ProGuard docs) |
Note: It is not clear what optimization R8 does, or how much control over that process is provided through the
-keep*
rules and theallowoptimization
modifier.
Several of the rules accept a class specification (class-spec
) which is a specification of classes and members that has a Java-like syntax.
For example:
-keepclassmembernames public class some.path.to.MyClass {
int intField;
android.content.Context getApplicationContext();
public static String *;
}
The syntax has strong support for filtering classes, methods, and fields.
The syntax supports class
(classes), interface
(interfaces), enum
(enumerations), and @interface
(annotations).
The special symbol <init>
is used to represent the name of a class’s constructor.
Wildcards and Special Characters
The syntax also supports wildcards and negation using special characters:
!
negates the condition described by the subsequent specification. Can be used with modifiers and with the class
, interface
, enum
, and @interface
keywords.*
a sequence of zero or more characters, other than package separators (.
), when used with other symbols in a pattern. Matches any reference type when used alone (this is not supported in all contexts in ProGuard).**
a sequence of zero or more characters, including package separators (.
), when used with other symbols in a pattern. Matches any reference type when used alone (does not match primitive types or void
).***
a sequence of zero or more characters, including package separators (.
), when used with other symbols in a pattern. Matches any reference type, primitive type, or void
when used alone.%
matches any primitive type (does not match void
) when used alone.?
matches any one character.<integer>
integer (starting at 1) referencing the value that matched a wildcard used earlier in the specification.-if
-predicated -keep*
rules, the index can reference any earlier wildcard match in the specification for either part....
matches any number of arguments when used within parentheses ((
and )
) of a method specification.For example:
-keepclassmembernames class * { long *UUID; } # don't rename long-valued fields ending with UUID in classes
Several other useful constructs are recognized in the class specification:
<fields>;
is a special string representing all fields<methods>;
is a special string representing all methodsNote: There are some differences between how the filter syntax is interpreted by R8 and ProGuard.
For example,*;
represents all fields and methods in both, but only R8 recognizes* *;
(all fields) and* *(...);
(all methods).
Modifiers
You can use the following modifier keywords to narrow down wildcards used in class specifications:
Name | Class | Method | Field |
---|---|---|---|
abstract |
✓ | ✓ | |
final |
✓ | ✓ | ✓ |
native |
✓ | ||
private |
✓ | ✓ | |
protected |
✓ | ✓ | |
public |
✓ | ✓ | ✓ |
static |
✓ | ✓ | |
strictfp |
✓ | ||
synchronized |
✓ | ||
transient |
✓ | ||
volatile |
✓ |
If multiple modifiers are used together on a single expression, then in most cases only classes, methods, or fields that match all of the applied modifiers will be matched.
However, if mutually exclusive modifiers are applied (e.g., private
and protected
), classes, method, and fields that match either of the mutually exclusive modifiers may be matched.
For example:
-keep public class * { # All public classes
public static *; # All public static fields in those classes
public protected abstract *(...); # All public or protected abstract methods in those classes
}
Subtype Matching and Annotated Matching
There are two powerful constructs that can be used with class filtering: subtype matching and annotated matching.
Specify either extends <type-name>
or implements <interface-name>
to match types that either extend or implement another type.
For example, -keep class * implements some.particular.SpecialInterface
will match all classes that implement SpecialInterface
.
Note that extends
and implements
can be used interchangeably.
Specify an annotation on the type filter to indicate that only types that are annotated with that annotation should match the filter.
For example, -keep @some.package.SomeAnnotation interface *
will match all interfaces that are annotated with @SomeAnnotation
.
There are several rules which control the naming of classes, methods, and fields:
Rule | Description |
---|---|
-keeppackagenames [<filter>] |
Don’t rename packages which match the filter. (ProGuard docs) |
-flattenpackagehierarchy [<name>] |
When renaming a class, move the package containing the class to a common base package with the specified name, or to the default package if no name is specified. Using -allowaccessmodification increases the number of classes which can be moved to a new package. (ProGuard docs) (See note) |
-repackageclasses [<name>] |
When renaming a class, move it to the named package, or to the default package if no package is named. (Overrides -flattenpackagehierarchy ) Using -allowaccessmodification increases the number of classes which can be moved to a new package. (ProGuard docs) (See note) |
-overloadaggressively |
Use the same name as much as possible, even if it may not be allowed by the source language. (ProGuard docs) |
-adaptclassstrings [<filter>] |
Update strings containing class names to use the new names. This can be filtered to only look for strings in certain classes. (ProGuard docs) |
-adaptresourcefilenames [<filter>] |
Rename Java resource files to match renamed classes. This can be filtered to look at particular files. (ProGuard docs) |
-adaptresourcefilecontents [<filter>] |
Update Java resource file contents to match renamed classes. This can be filtered to look at particular files. (ProGuard docs) |
Flatten vs. Repackage
There is a subtle difference between -flattenpackagehierarchy
and -repackageclasses
.-repackageclasses
moves the classes into a single package.-flattenpackagehierarchy
renames the packages to be based on the name, keeping classes in their own package.
Given three classes:
com.example.packageOne.ClassOne
com.example.packageOne.subPackageOne.ClassTwo
com.example.packageTwo.ClassThree
-repackageclasses "go.here"
will result in:
com.example.packageOne.ClassOne -> go.here.a:
com.example.packageOne.subPackageOne.ClassTwo -> go.here.b:
com.example.packageTwo.ClassThree -> go.here.c:
-flattenpackagehierarchy "go.here"
will result in:
com.example.packageOne.ClassOne -> go.here.a.a:
com.example.packageOne.subPackageOne.ClassTwo -> go.here.b.a:
com.example.packageTwo.ClassThree -> go.here.c.a:
R8 will provide new names by cycling through the English alphabet.
By using dictionaries, it is possible to control, to a degree, how R8 will determine the new names for classes, methods, and fields.
Rule | Description |
---|---|
-classobfuscationdictionary <filename> |
Use the specified file to find new names for classes. (ProGuard docs) |
-obfuscationdictionary <filename> |
Use the specified file to find new names for methods and fields. (ProGuard docs) |
-packageobfuscationdictionary <filename> |
Use the specified file to find new names for packages. (ProGuard docs) |
Dictionary Files
The dictionary files contain lists of unique names separated by whitespace or punctuation.
A #
can be used to specify a comment.
The filename specified should be relative to the directory containing the rules file.
The names must consist of characters allowed for Java identifiers.
a1, a2, a3 #A few identifiers
class package for while do if else switch goto this null #Reserved word identifiers
#Identifiers on their own lines
q
w
e
r
t
y
Mapping Files
Map files contain direct links between the original and new names of classes, methods, and fields.
Rule | Description |
---|---|
-applymapping <filename> |
Use the specified map for renaming. (ProGuard docs) |
-printmapping [<filename>] |
Print a mapping from the original to the new names to the specified file, or to stdout if there is no file specified. (ProGuard docs) (See note) |
-printmapping
Regardless of the -printmapping
rule, maps will always be output to a variant specific file (e.g.build/outputs/mapping[/r8][/{flavorName}]/{buildType}/mapping.txt
).
If -printmapping
is configured to print to a file in a configuration that is used by more than one variant, the configured file will be overwritten to reflect whichever variant built last.
Some ProGuard rules are unsupported by R8 and will not be honored.
The following rules will cause R8 to issue an error:
-microedition
-skipnonpubliclibraryclasses
includecode
(modifier used with -keep*
rules) (See issue)The following rules will cause R8 to issue a warning message:
-optimizationpasses
(enabled by proguard-android-optimize.txt
)-optimizations
The following rules are ignored:
-addconfigurationdebugging
-android
-assumenoescapingparameters
-assumenoexternalreturnvalues
-assumenoexternalsideefffects
-dontpreverify
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontusemixedcaseclassnames
-dump
-forceprocessing
-keepparameternames
-mergeinterfacesaggressively
-outjars
-target
-useuniqueclassmembernames
-verbose
R8 is designed for use with Android projects.
However, some rules it supports are not applicable in Android projects:
Rule | Description |
---|---|
-keepdirectories [<filter>] |
Keep directory entries in the output jar or zip file. (ProGuard rules) |
This section describes some issues that you might encounter when trying to use R8.
You might also find details about your issue in the Android Studio User Guide, the R8 Compatibility FAQ, or ProGuard’s Troubleshooting page.
If you cannot find a solution to your problem, or if you otherwise encounter incorrect behavior, you can report a bug with the Google team.
Make sure that you do not have android.enableR8=false
in your gradle.properties
file.
Note: If you are using a version of the Android Gradle Plugin prior to 3.4, you will need to set
android.enableR8=true
in yourgradle.properties
file to enable R8.
This rule is unsupported in R8.
Remove it from your configuration.
Make sure that any rule file that you want to use is properly configured in your Gradle build script with proguardFiles
.
Relative paths configured in your Gradle build script should be relative to the application or library module for which you would like the rules to apply.
See the Android Studio User Guide for details.
Note: The Android Gradle Plugin will not error or warn you if it cannot locate the specified file.
R8 issues this warning if you use an extends
rule to match descendents of an interface rather than implements
, regardless of whether the descendents you’re trying to match are classes or interfaces.
If the specified rule is a custom rule that you have created, you can update the rule to use implements
rather than extends
.
However, some libraries, including Android support libraries, contain rules that will produce this warning.
Unfortunately, there is no easy way to resolve or suppress the warning in that case.