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.
Android builds use R8 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:
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.
android_binary and android_applicationR8 is enabled and configured using attributes on the android_binary rule (to produce an APK) or the android_application rule (to produce an Android App Bundle / AAB). Both rules accept the same R8 optimization 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: 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.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.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 project in src/main/BUILD:
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"], )
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.
# 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.**
Run the following command to build the optimized binary:
bazel build //path/to:r8-optimized-app
Bazel places build artifacts in the bazel-bin output directory:
bazel-bin/path/to/r8-optimized-app.apk (or .aab when using android_application), containing the shrunk and optimized app.bazel-bin/path/to/r8-optimized-app_proguard.map (generated when proguard_generate_mapping = True), containing mapping data for stack trace de-obfuscation.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.proguard-rules.pro, e.g. -printseeds <file> and -printusage <file>.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.
The 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.
To generate the Configuration Analyzer report, build your target with the optimization_config_analyzer output group:
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.
The Configuration Analyzer report calculates three primary scores that represent the percentage of your codebase available for optimization:
Use the report to systematically optimize your configuration:
-keep class com.example.** { *; }) and refine them to target only the specific members accessed via reflection or JNI.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.
If you encounter issues or unexpected behavior when running your R8-optimized app, refer to the following R8 guides: