Five years on from the first wave of ProGuard guides, the modern Android shrinker story is R8 full mode. The Android Gradle plugin now defaults to R8 for shrinking, obfuscation, and optimisation, but most projects still ship with legacy settings quietly wasting megabytes.
This piece walks through the current R8 workflow. The aim: small APKs without naive keep rules that defeat the shrinker.
Why R8 full mode is the baseline
R8 full mode has been the default for any project built with AGP 8.0 or newer. It performs aggressive optimisations like changing class visibility, inlining methods, and removing unused interfaces, all in one pass that combines what ProGuard used to do in two. Compatibility mode exists to emulate legacy ProGuard behaviour, but it is slower and less effective.
Check gradle.properties for the line `android.enableR8.fullMode=false`. If it exists, delete it. You are opting out of free savings.
The configuration file that actually matters
Most tutorials tell you to ship with the default proguard-android.txt. The recommended approach switches to proguard-android-optimize.txt inside the release build type:
“`
BuildTypes {
GetByName("release") {
IsMinifyEnabled = true
IsShrinkResources = true
ProguardFiles(
GetDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
“`
The optimise variant unlocks additional bytecode-level passes that the plain file skips. No runtime cost, just smaller DEX output.
Keep rules without wrecking R8
This is where most projects fall apart. R8 is a static analyser and cannot see reflection, so the default reaction is to keep entire packages:
“`
# BAD PRACTICE
-keep class com.example.mydata.** { *; }
“`
That defeats R8 entirely. The right shape is to keep the class but allow shrinking and renaming inside it:
“`
-keep,allowshrinking,allowobfuscation class com.example.mydata.User
“`
For Kotlin data classes, the @Keep annotation lives next to the code it protects:
“`
@Keep
Data class User(val id: String, val name: String)
“`
Cut resource weight beyond code
Code is rarely the heaviest part of an APK. Resources are. Three levers matter:
- `isShrinkResources = true` strips unused drawables, layouts and strings.
- `resourceConfigurations += setOf("en", "hi")` drops localisation for languages you do not support.
- Converting PNGs to WebP through Android Studio's built-in converter can shrink image weight substantially without visible quality loss.
App bundles do the rest
Stop uploading APKs to the Play Store. App Bundles let Google Play generate per-device APKs, so a low-end phone does not pull xxxhdpi assets it will never display. Downloads can shrink by 15 to 20 percent.
Who should care
| Team type | Why this matters | Priority |
|---|---|---|
| Apps targeting emerging markets | Smaller APKs mean lower data costs for users on limited plans | High |
| High-growth startups | Faster install = higher Play Store conversion rates | High |
| Library maintainers | Consumer rules must play nice with narrow app-level keep rules | Medium |
| Enterprise apps with JNI or reflection | Manual testing of release builds is non-negotiable | Medium |
| Apps with only English resources | Resource configurations set alone can cut 10-30% | High |
Our analysis
R8 full mode is free performance. Projects still using `proguard-android.txt` instead of `proguard-android-optimize.txt` are paying real costs in download size, install time, and store conversion for no reason.
Two real risks deserve attention. First, reflection-heavy code without proper keep rules will crash in release builds. R8 cannot see what it cannot analyse. Second, libraries that ship consumer ProGuard rules sometimes conflict with narrow app-level rules. Test release builds as part of your CI matrix, not just after a problem surfaces.
The trade-off is configuration complexity. Narrow keep rules take more thought than blanket `-keep` statements, but the savings compound across every user who downloads your app. Teams shipping large APKs in 2026 are not running out of options; they are running on stale defaults.
What to watch: future AGP versions keep tightening R8's optimiser passes. Code that needs to keep a specific name for reflection should be annotated with `@Keep` rather than buried in a wildcard rule that R8 cannot reason about.
Bottom line
R8 full mode plus proguard-android-optimize.txt plus narrow keep rules plus App Bundles is the modern Android shrinker workflow. The configuration is straightforward; the savings are not.
Key takeaways
- Delete `android.enableR8.fullMode=false` from gradle.properties if it exists
- Switch to proguard-android-optimize.txt instead of the default proguard-android.txt
- Replace package-level -keep rules with class-level rules using allowshrinking and allowobfuscation
- Trim resources with isShrinkResources and a tight resourceConfigurations set
- Ship an Android App Bundle rather than a raw APK
Frequently asked questions
Is R8 full mode enabled by default?
Yes. R8 full mode has been the default since AGP 8.0. The line android.enableR8.fullMode=false disables it and should be deleted.
What is the difference between proguard-android.txt and proguard-android-optimize.txt?
The optimise variant enables additional bytecode-level optimisations that the standard file skips, at no runtime cost.
How do I keep reflection-based classes safe?
Use class-level -keep rules with allowshrinking and allowobfuscation modifiers, or annotate the class with @Keep in Kotlin.
Why are Android App Bundles better than APKs?
Bundles let Google Play generate per-device APKs, which trims unused assets and typically reduces download size by 15 to 20 percent.
Does R8 full mode affect debug builds?
No. The settings in this guide apply to the release build type. Debug builds remain unminified for fast iteration.