blob: f5490b6976c7f4d257ce9cc60470d38246a62df5 [file] [view]
# Shrinking and Optimization with R8
This page covers how to configure code shrinking, resource shrinking, bytecode
optimization, and obfuscation using **R8** with `rules_android`.
_If you're new to building Android apps with Bazel, start with the [Android App
Tutorial](https://bazel.build/start/android-app)._
## Overview
Android builds use
**[R8](https://developer.android.com/topic/performance/app-optimization/keep-rules-overview)**
to reduce application size, decrease runtime memory usage, and improve
performance. By eliminating unused code and resources, R8 reduces both the
on-device download size and the runtime memory footprint of your application.
R8 performs four core functions during the build process:
* **Code shrinking (tree shaking):** Detects and safely removes unused
classes, fields, methods, and attributes from your app and its library
dependencies, reducing DEX size and runtime memory consumption.
* **Resource shrinking:** Removes unused resources (such as drawables,
layouts, and strings) packaged in your app. Resource shrinking works in
tandem with code shrinking to reduce on-disk and in-memory asset overhead.
* **Bytecode optimization:** Analyzes and optimizes bytecode instructions to
reduce DEX code size and improve runtime efficiency on the Android
Runtime (ART).
* **Obfuscation (name minification):** Renames classes, fields, and methods
with short, obfuscated names (such as `a`, `b`, `c`), reducing DEX file size
and making reverse engineering more difficult.
Note that while R8 is the modern tool used under the hood (replacing
**ProGuard**), configuration attributes in `android_binary` and
`android_application` rules still retain the `proguard_` prefix (such as
`proguard_specs` and `proguard_generate_mapping`) for historical compatibility
with ProGuard configuration rule syntax.
## Configuring R8 in `android_binary` and `android_application`
R8 is enabled and configured using attributes on the
[`android_binary`](https://bazelbuild.github.io/rules_android/#android_binary)
rule (to produce an APK) or the
[`android_application`](https://bazelbuild.github.io/rules_android/#android_application)
rule (to produce an Android App Bundle / AAB). Both rules accept the same R8
optimization attributes.
### Key attributes
* `proguard_specs`: A list of labels pointing to ProGuard/R8 configuration
files containing keep rules and optimization directives. Specifying this
attribute enables R8 code shrinking and optimization. Typically, this
includes:
* [`proguard-android-optimize.txt`](https://github.com/bazelbuild/rules_android/blob/main/examples/r8-optimized/proguard-android-optimize.txt):
Contains standard recommended Android app optimizations and default keep
rules (equivalent to the default configuration provided by the Android
Gradle Plugin). You can download it from the example repository and place it
in your project.
* `proguard-rules.pro`: An empty file where you add custom keep rules
specific to your app, following the guide on [adding keep
rules](https://developer.android.com/topic/performance/app-optimization/add-keep-rules).
* `shrink_resources`: A boolean indicating whether to enable resource
shrinking. When set to `True`, unused Android resources are removed from the
packaged APK or AAB. *Note: Resource shrinking requires `proguard_specs` to
be enabled.*
* `proguard_generate_mapping`: A boolean indicating whether Bazel should
generate a mapping file (`_proguard.map`) that maps obfuscated class and
method names back to their original source names. This is essential for
de-obfuscating crash stack traces in production.
### Recommended target structure
Because R8 optimization increases build times, a best practice is to declare a
separate optimized target for release builds while using an unoptimized target
during daily iterative development.
The following example `BUILD` configuration can be added directly to the
[Android App Tutorial](https://bazel.build/start/android-app) project in
`src/main/BUILD`:
```starlark
load("@rules_android//rules:rules.bzl", "android_binary")
# Unoptimized target for faster local build time and testing
android_binary(
name = "app",
manifest = "//src/main/java/com/example/bazel:AndroidManifest.xml",
deps = ["//src/main/java/com/example/bazel:greeter_activity"],
)
# Optimized target for release and performance testing
android_binary(
name = "r8-optimized-app",
manifest = "//src/main/java/com/example/bazel:AndroidManifest.xml",
proguard_generate_mapping = True,
proguard_specs = [
"proguard-android-optimize.txt",
"proguard-rules.pro",
],
shrink_resources = True,
deps = ["//src/main/java/com/example/bazel:greeter_activity"],
)
```
## Configuring keep rules
R8 inspects all reachable entry points in your application. However, code or
resources accessed dynamically at runtime (such as via reflection, JNI native
methods, or XML layout references) might appear unused to static analysis and
could be stripped or renamed inadvertently.
To prevent R8 from removing or obfuscating required code, define **keep rules**
in your `proguard-rules.pro` file (initially created as an empty file alongside
your `BUILD` file). For detailed instructions and best practices, see the
official Android guide on
[how to add keep rules](https://developer.android.com/topic/performance/app-optimization/add-keep-rules).
### Common keep rule examples
```
# Preserve a class and all its public/protected methods and fields
-keep class com.example.bazel.model.** {
public protected *;
}
# Preserve class members accessed via reflection
-keepclassmembers class com.example.bazel.data.UserData {
<fields>;
}
# Preserve native JNI methods
-keepclasseswithmembernames class * {
native <methods>;
}
# Suppress warnings from third-party dependencies with incomplete references
-dontwarn com.example.thirdparty.**
```
## Building and inspecting outputs
Run the following command to build the optimized binary:
```bash
bazel build //path/to:r8-optimized-app
```
### Build outputs
Bazel places build artifacts in the `bazel-bin` output directory:
* **Optimized APK or AAB:** `bazel-bin/path/to/r8-optimized-app.apk` (or
`.aab` when using `android_application`), containing the shrunk and
optimized app.
* **ProGuard Mapping File:** `bazel-bin/path/to/r8-optimized-app_proguard.map`
(generated when `proguard_generate_mapping = True`), containing mapping data
for stack trace de-obfuscation.
* **Optimization Config Analyzer Report:**
`bazel-bin/path/to/r8-optimized-app_optimization_report.html` (generated when
requesting the `--output_groups=optimization_config_analyzer` output group),
an interactive visual HTML report analyzing R8 configuration quality and keep
rule impact.
* **Optional diagnostic files:** Diagnostic files such as seeds and usage
lists indicating which classes and members were kept or removed can
optionally be configured via flags in `proguard-rules.pro`, e.g.
`-printseeds <file>` and `-printusage <file>`.
### App size impact
For small sample applications, the difference in APK or AAB size between
unoptimized and optimized builds may be minimal. However, as an application
grows and incorporates larger third-party dependencies (such as Guava, AndroidX,
or gRPC), R8's tree shaking and resource shrinking can significantly reduce the
final download and install size.
## R8 Configuration Analyzer
The
**[R8 Configuration Analyzer](https://developer.android.com/topic/performance/app-optimization/r8-configuration-analyzer)**
is a diagnostic tool designed to help you maximize R8's performance benefits by
providing detailed insights into your application's optimization quality and the
impact of each keep rule.
Because keep rules prevent R8 from shrinking, optimizing, and obfuscating code,
broad or overly conservative rules can significantly reduce optimization effectiveness. The
Configuration Analyzer produces an interactive HTML report to help you audit and
refine your rules.
### Generating the report
To generate the Configuration Analyzer report, build your target with the
`optimization_config_analyzer` output group:
```bash
bazel build //path/to:r8-optimized-app --output_groups=optimization_config_analyzer
```
Bazel generates the report at:
```
bazel-bin/path/to/r8-optimized-app_optimization_report.html
```
You can open this HTML file directly in any web browser to inspect the results.
### Understanding the report metrics
The Configuration Analyzer report calculates three primary scores that represent
the percentage of your codebase available for optimization:
* **Shrinking score:** The percentage of classes, fields, and methods that R8
is permitted to remove if unused. A higher score means fewer unreachable
classes and fewer members are kept unnecessarily.
* **Optimization score:** The percentage of code available for R8 bytecode
optimizations (such as method inlining, dead-code removal, and class
merging). Improving this score helps reduce app startup latency and runtime
memory usage.
* **Obfuscation score:** The percentage of classes, fields, and methods
available for name minification, reducing DEX metadata overhead.
### Refining keep rules with the report
Use the report to systematically optimize your configuration:
1. **Identify broad keep rules:** View the list of keep rules sorted by the
number of classes, methods, and fields they prevent from being optimized.
Look for broad wildcards (such as `-keep class com.example.** { *; }`) and
refine them to target only the specific members accessed via reflection or
JNI.
2. **Audit third-party library rules:** Third-party dependencies may bundle
conservative consumer keep rules. The report attributes each rule to its
source configuration file, helping you identify libraries that
disproportionately hinder optimization.
3. **Resolve subsumed rules:** The analyzer highlights overlapping rules where
a broad rule subsumes a narrower rule (for instance, a package-wide wildcard
rule that overlaps a single-class rule). You can remove redundant rules or
narrow the broader rule to unlock optimizations.
4. **Prune unused and duplicate rules:** Identify **unused rules** (rules that
match zero classes or members in your build) and **identical rules**
duplicated across configuration files, keeping your configuration clean and
maintainable.
After refining rules in your `proguard-rules.pro`, re-run the build with
`--output_groups=optimization_config_analyzer` to verify score improvements, and
run your test suite to ensure runtime functionality is preserved.
## Troubleshooting & testing
If you encounter issues or unexpected behavior when running your R8-optimized
app, refer to the following R8 guides:
* [Troubleshoot the
optimization](https://developer.android.com/topic/performance/app-optimization/troubleshoot-the-optimization)
– General guidance on diagnosing shrinking and optimization issues.
* [Troubleshooting
rules](https://developer.android.com/topic/performance/app-optimization/troubleshooting-rules)
– Instructions on debugging and fixing missing keep rules.
* [Test the
optimization](https://developer.android.com/topic/performance/app-optimization/test-the-optimization)
– Best practices for validating and testing optimized builds before
publishing.
## Further reading
* [Android App Tutorial](https://bazel.build/start/android-app) – Step-by-step
walkthrough of building Android apps with Bazel.
* [R8 Configuration
Analyzer](https://developer.android.com/topic/performance/app-optimization/r8-configuration-analyzer)
– Official Android guide to using the R8 Configuration Analyzer and refining
keep rules.
* [Fast Iterative Development with mobile-install](https://bazel.build/docs/mobile-install)
– Accelerate Android development cycles.
* [Android R8 Keep Rules
Overview](https://developer.android.com/topic/performance/app-optimization/keep-rules-overview)
– Official Android guide to customizing R8 rules.
* [Adding Keep Rules
Guide](https://developer.android.com/topic/performance/app-optimization/add-keep-rules)
– Detailed guide on authoring custom keep rules for your application.
* [rules_android Stardoc](https://bazelbuild.github.io/rules_android/) – API
and attribute documentation for rules_android.