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.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.
If you encounter issues or unexpected behavior when running your R8-optimized app, refer to the following R8 guides: