ProGuard vs R8
What Is Code Shrinking?
When you build a release APK, the build tool processes your code to:
- Shrink — Remove unused classes, methods, and fields from dependencies
- Obfuscate — Rename classes and methods to meaningless short names
- Optimize — Apply bytecode optimizations like inlining and dead code elimination
This reduces your app's final size and makes reverse engineering harder.
ProGuard vs R8
| Feature | ProGuard | R8 |
|---|---|---|
| Maintained by | Guardsquare (open source) | Google (built into AGP) |
| Speed | Slower | Faster (15-20% build speedup) |
| Default since | N/A | AGP 3.4+ |
| Optimization | Good | Improved (better inlining) |
Since Android Gradle Plugin 3.4, R8 is the default. Your proguard-rules.pro file works with R8 without changes.
Build Configuration
// app/build.gradle.kts
android {
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
isMinifyEnabled— Enables shrinking, obfuscation, and optimizationisShrinkResources— Removes unused resourcesproguard-android-optimize.txt— Google's default rules with optimizations
The Mapping File
R8 generates mapping.txt in app/build/outputs/mapping/release/. Upload it to the Google Play Console or your crash reporting tool to deobfuscate stack traces.
com.example.app.User -> a.b.c:
java.lang.String name -> a
java.lang.String email -> b
void login() -> c
Keep Rules and Library Configuration
Understanding Keep Rules
By default, R8 removes anything it doesn't detect as reachable from your code. If a class is accessed via reflection, serialization, or native code, R8 won't know about it and may strip or rename it. Keep rules tell R8 what to preserve.
Common Rule Syntax
# Keep a specific class and all its members
-keep class com.example.MyClass { *; }
# Keep all classes in a package
-keep class com.example.api.** { *; }
# Keep classes with a specific annotation
-keep @com.example.SerializedName class * { *; }
# Don't obfuscate (but still allow shrinking)
-dontobfuscate
Rules for Common Libraries
Retrofit:
-keepattributes Signature
-keepattributes *Annotation*
-keep class retrofit2.** { *; }
-keepclasseswithmembers class * {
@retrofit2.http.* <methods>;
}
Gson:
-keepattributes Signature
-keepattributes *Annotation*
-keep class com.google.gson.** { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
Room:
-keep class * extends androidx.room.RoomDatabase
-keep @androidx.room.Entity class *
-keep @androidx.room.Dao class *
Debugging R8 Issues
When your app crashes in release but works in debug:
- Check the stack trace — Use the mapping file to deobfuscate
- Add the missing keep rule — Find the stripped class in build warnings
- Enable verbose logging — Add
-verbosetoproguard-rules.pro - Temporary fix — Use
-keep class ** { *; }to identify the problematic class
Obfuscation as a Security Layer
What Obfuscation Protects Against
Obfuscation renames classes and methods to short, meaningless names. It makes reverse engineering harder but not impossible. It protects against:
- Casual inspection — Most attackers won't bother reading obfuscated code
- Automated tools — Generic decompilers produce unreadable output
- IP theft — Proprietary algorithms and business logic are harder to extract
What Obfuscation Does NOT Protect Against
- Determined attackers with time and motivation
- Runtime analysis and dynamic instrumentation
- Network traffic interception (use certificate pinning instead)
- Data stored in plaintext (use encryption instead)
Additional Security Measures
# Remove logging in release builds
-assumenosideeffects class android.util.Log {
public static int v(...);
public static int d(...);
public static int i(...);
}
This strips Log statements from your release APK, preventing information leakage.
Best Practices
- Always enable minification for release builds — No exceptions
- Upload the mapping file to your crash reporting tool
- Test release builds regularly — Don't wait until launch to discover R8 issues
- Keep rules are additive — Library dependencies contribute their own rules automatically
- Use
-keepsparingly — Over-keeping defeats the purpose of shrinking
Quiz
1. Since which Android Gradle Plugin version is R8 the default code shrinker?
2. Why does a class accessed via reflection crash in release builds but not debug?
3. What does -assumenosideeffects do in a ProGuard/R8 rules file?
4. Where does R8 generate the mapping file for deobfuscation?
Flashcards
Question
What are the three main tasks R8 performs on your code?
Click to reveal answer
Answer
Shrinking (removing unused code), obfuscation (renaming to short names), and optimization (inlining, dead code elimination).
Question
Why do libraries like Gson and Retrofit need keep rules?
Click to reveal answer
Answer
They use reflection, annotations, and dynamic proxies at runtime. R8 static analysis cannot detect these accesses, so keep rules are needed to prevent stripping or renaming.
Question
What is the mapping file used for?
Click to reveal answer
Answer
It maps obfuscated class and method names back to their original names, enabling deobfuscation of stack traces from crash reports in release builds.
Question
What does isShrinkResources do in build.gradle?
Click to reveal answer
Answer
Removes unused resources (images, strings, layouts) from the final APK, further reducing app size beyond code shrinking alone.
Revision Notes
Key Takeaways
- 1. R8 is the default code shrinker since AGP 3.4, fully compatible with ProGuard rules
- 2. Always enable isMinifyEnabled for release builds to shrink and obfuscate code
- 3. Libraries using reflection (Gson, Retrofit, Room) need keep rules to survive R8
- 4. Upload the mapping file to deobfuscate crash reports from release builds
- 5. Strip logging statements in release builds to prevent information leakage
Interview Tips
- • Explain the difference between shrinking, obfuscation, and optimization
- • Know why reflection-based libraries need keep rules
- • Be ready to debug R8 crashes using the mapping file
- • Discuss why obfuscation is a defense layer, not a security solution
Cheat Sheet
ProGuard / R8 Cheat Sheet
R8 is default since AGP 3.4 — Drop-in replacement for ProGuard.
Build Config:
- isMinifyEnabled = true (shrinking + obfuscation)
- isShrinkResources = true (unused resource removal)
- proguard-android-optimize.txt (default rules)
Keep Rules:
-keep class X { *; }— preserve class and members-keep class X.**— preserve package recursively-keep @Annotation class *— keep annotated classes-dontobfuscate— disable renaming only
Common Library Rules:
- Retrofit: keep @retrofit2.http methods + Signature
- Gson: keep @SerializedName fields + Signature
- Room: keep Entity, Dao, RoomDatabase subclasses
Debugging:
- mapping.txt in build/outputs/mapping/release/
- Upload to crash reporter and Play Console
- Strip logs with -assumenosideeffects
Security:
- Strip Log.v/d/i in release builds
- Obfuscation is not encryption — layer with encryption