This is unofficial documentation for R8, Google's code shrinker for Android™. Google intends R8 to be a drop-in replacement for ProGuard, and, as R8 is designed to be compatible with most ProGuard rules, the ProGuard Manual is a valuable reference for R8. However, there still are substantial differences between R8 and ProGuard, and Google has not documented those differences. This site is meant to fill that gap.
This site is open source on GitHub™ and we encourage you to contribute by opening issues or submitting pull requests.
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.4.77 using Android Gradle Plugin v3.4.0-rc03.
PreEmptive Solutions is the developer of PreEmptive Protection™ - DashO™, which provides powerful obfuscation and shielding for Android applications and libraries.
You can enable R8 in your Android project for a build type by using the minifyEnabled
setting in your application's or library's Gradle build script:
android {
buildTypes {
release {
minifyEnabled true
}
}
}
R8 defaults to using ProGuard-compatible optimizations, but you can enable additional optimizations by enabling R8's Full Mode in your gradle.properties
file (Learn More):
android.enableR8.fullMode=true
You can specify any number of R8 configuration files for a build type or product flavor using the proguardFiles
setting in your application's or library's Gradle build script:
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'custom-rules.txt'
}
}
}
The getDefaultProguardFile()
method specifies a configuration file provided by the Android Gradle Plugin that contains basic rules suitable for Android builds.
You can use 'proguard-android.txt'
for the default rule set used by the Android Gradle Plugin, or 'proguard-android-optimize.txt'
to enable optimization.
You can find these default files in the build/intermediates/proguard-files
directory within your application or library module's directory after running a build.
Note that while the files in this proguard-rules
directory have version numbers affixed to their name, you should not include these version numbers in the file name in your Gradle build script.
In this example, 'custom-rules.txt'
refers to a configuration file named "custom-rules.txt" in the same directory as your application's or library's build.gradle
file.
You can specify your own R8 rules in such a file.
NOTE: The Android Gradle Plugin will generate additional rules based on references to classes in your application's or library's manifest and resources. If no
proguardFile
orproguardFiles
configuration is provided, R8 will also add the configuration from the defaultproguard-android.txt
file.
You can also configure flavor specific rules using proguardFile
:
android {
flavorDimensions 'monetization'
productFlavors {
free {
dimension 'monetization'
proguardFile 'free-rules.txt'
}
paid {
dimension 'monetization'
proguardFile 'paid-rules.txt'
}
}
}
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) |
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 class some.path.to.MyClass {
int intField;
android.content.Context getApplicationContext();
}
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.
The syntax also supports wildcards and negation using special characters :
!
negates the condition described by the subsequent specification.*
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 orvoid
).***
a sequence of zero or more characters, including package separators (.
), when used with other symbols in a pattern. Matches any reference type, primitive type, orvoid
when used alone.%
matches any primitive type (does not matchvoid
) when used alone.?
matches any one character.<integer>
integer (starting at 1) referencing the value that matched a wildcard used earlier in the specification. For-if
-predicated-keep*
rules, the index can reference any earlier wildcard match in the specification for either part. Neither R8 nor ProGuard seem to handle back references in the presence of wildcards in both the class name and class member names. R8 does not appear to handle back references within member specifications....
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
Note that R8 does not currently respect negation (!
) of class member expressions in class specifications for the -if
, -keepclasseswithmembers
, and -keepclasseswithmembernames
(See issue).
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
.
Several other useful constructs recognized in the class specification:
<fields>;
is a special string representing all fields<methods>;
is a special string representing all methods
NOTE: 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).
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) (See issue) |
-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) |
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) |
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
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) (See issue) |
-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) |
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 that 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)
The following rules will cause R8 to issue a warning message:
-optimizationpasses
(enabled byproguard-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) |
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.
Make sure that you have set minifyEnabled
for all of the build types on which you want R8 to run.
See Enabling R8 for details.
This rule is unsupported in R8. Remove it from your configuration.
Assuming that this occurs only when you are using R8 (or ProGuard), R8 has not detected that the class described in the exception message is used or has renamed it in a way that breaks reflection calls.
R8 detects most straightforward reflection calls, but doesn't necessarily catch all cases.
Use a -keep
rule to prevent the class from being removed or renamed.
Make sure that any rule file that you want to use is properly configured in your Gradle build script with proguardFiles
or proguardFile
.
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 Specifying R8 Configuration Files 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 and unfortunately there is no easy way to resolve or suppress this warning in that case.