Skip to content

minsko/r8-docs

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

76 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Introduction

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.

Assumptions

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.

Who We Are

PreEmptive Solutions is the developer of PreEmptive Protection™ - DashO™, which provides powerful obfuscation and shielding for Android applications and libraries.

Enabling R8

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

Specifying R8 Configuration Files

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 or proguardFiles configuration is provided, R8 will also add the configuration from the default proguard-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'
        }
    }
}

General Rules

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)

Keep Rules

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 the allowoptimization modifier.

Class Specification

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 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. 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).

Renaming Configuration

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)

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:

Dictionaries

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) (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)

-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 that one variant, the configured file will be overwritten to reflect whichever variant built last.

Unsupported Rules

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 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

Not Applicable

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)

Troubleshooting

ProGuard appears to be running instead of R8

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 your gradle.properties file to enable R8.

Neither R8 nor ProGuard appears to be enabled

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.

D8: Unsupported option: -skipnonpubliclibraryclasses

This rule is unsupported in R8. Remove it from your configuration.

Unexpected java.lang.ClassNotFoundException at runtime

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.

Custom rules don't appear to be used

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.

The rule [some rule] uses extends but actually matches implements.

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.

About

Unofficial documentation for R8

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • CSS 63.5%
  • HTML 21.9%
  • JavaScript 14.1%
  • Ruby 0.5%