Protect your APK. Optimize with confidence.
Write Proguard Rules For Your Android App
Learn how to write effective ProGuard rules, avoid crashes in release builds, and keep your Jetpack Compose + Hilt + Retrofit app secure, optimized, and lean.
Not a Medium Member? “Read for Free”
When you build your app for release (the version you upload to Play Store), Android tries to make your app smaller and harder to reverse-engineer using a tool called R8 (which includes ProGuard features).
So what does it do?
R8 (ProGuard) performs three main tasks:
1). Shrinking
It removes unused code — classes, functions, and libraries that your app doesn’t use.
Example:
You include a library with 100 functions but only use 5.
R8 deletes the unused 95 functions to reduce APK size.
2). Obfuscation
It renames your class and method names to make them unreadable.
Example:
Your code:
class LoginManager {
fun authenticateUser() { ... }
}After obfuscation:
class a {
fun b() { ... }
}This makes it harder for hackers to reverse-engineer your app.
It only renames those classes and function which known at compile time
3). Optimization
It rewrites parts of your code to run faster and better.
When you open your build.gradle(Module:app) , you will see
release {
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}This line tells the Android build system (Gradle) to include two sets of ProGuard rules when building your app’s release version:
1). getDefaultProguardFile(“proguard-android-optimize.txt”)
This is a default file provided by Android (Google).
It contains a set of common, safe optimizations for Android apps.
It includes:
- Basic shrinking (removing unused code)
- Safe obfuscation (renaming classes/methods)
- Some performance optimizations
This is like a starter pack of rules. Google maintains this so you don’t have to write everything yourself.
2). "proguard-rules.pro"
This is your custom ProGuard file — the one where you write your own rules.
Why do you need this?
Because the default file:
- Doesn’t know about your app-specific logic
- Doesn’t know what libraries you’re using
- Can’t predict what classes you want to keep
So, you use proguard-rules.pro to say things like:
# Don’t remove this model — it’s used with Gson
-keep class com.myapp.models.** { *; }
# Don’t obfuscate these methods
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}By default this file (
proguard-rules.pro) is empty.
When You NEED to write ProGuard rules :
Before writing proguard rules for your code , you have to enable isMinifyEnable = true in build.gradle file.
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}Now you have to write proguard rules for your code , there is no choice, otherwise release build can crash at run time.
1. You’re using Reflection (Gson, Moshi, Firebase, Retrofit, etc.)
Reflection means something in your code is being accessed at runtime by name — not directly through code.
Get Jayant Kumar🇮🇳’s stories in your inbox
Join Medium for free to get updates from this writer.
Example: Gson needs model class names and fields to be untouched so it can serialize/deserialize properly.
# Keep all models used with Gson
-keep class com.example.model.** { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}2. You’re using Dependency Injection frameworks (like Hilt or Dagger)
These frameworks generate code and reference classes via annotations — so if ProGuard removes or renames them, it breaks.
# Keep Hilt-related classes
-keep class dagger.hilt.** { *; }
-keep class javax.inject.** { *; }
-keep class com.google.dagger.** { *; }
-dontwarn dagger.**3. You’re using Jetpack Compose
Compose uses a lot of compiler-generated classes and annotations.
Jetpack Compose is mostly ProGuard-safe by default, but:
- If you use reflection (e.g., with
@Previewor libraries that inspect Compose code) - Or you’re using
rememberSaveablewith custom types
Then you may need to keep those classes.
# Keep all Compose-related classes (mostly safe)
-keep class androidx.compose.** { *; }
-dontwarn androidx.compose.**4. You’re using Room (or other annotation processors)
Room generates code based on annotations. You must keep entities and DAOs.
# Keep Room entities and DAOs
-keep class androidx.room.** { *; }
-keep @androidx.room.* class * {*;}
-dontwarn androidx.room.**5. You’re using External SDKs or Libraries
Some SDKs (like Firebase, Facebook SDK, etc.) require you to add special rules. If you don’t, the app may crash or fail silently.
Always check the library’s documentation for required ProGuard rules.
6. You’re using reflection manually
If you’re calling things like:
val clazz = Class.forName("com.example.SomeClass")You need to keep SomeClass or ProGuard will remove or rename it.
What Happens if You Don’t Write Any Rules?
When you enable isMinifyEnabled = true in build.gradle file , ProGuard (or R8, more precisely) tries to:
- Remove unused code
- Rename (obfuscate) class/method names
- Inline and optimize
It doesn’t know if a class is being used via reflection, annotations, or by code generated at compile-time (like Hilt, Room, Compose, etc.).
So if it sees:
val user = User() // used normallyProGuard says: “Cool, keep this.”
But if it doesn’t see direct usage (for example, User is accessed via reflection or annotation):
val user = Gson().fromJson(json, User::class.java)ProGuard says: “Hmm… looks unused. Let me remove or rename it.”
Result: Your app compiles fine, but crashes at runtime.
Usually only in release builds, which is very hard to debug.
proguard-rules.pro for Jetpack Compose + Hilt + Retrofit + Room + Gson
# ----------- KEEP ANNOTATIONS (Compose, Hilt, etc.) -----------
-keepattributes *Annotation*, InnerClasses, Signature, EnclosingMethod
# ----------- KEEP Jetpack Compose -----------
# Compose compiler generates synthetic methods that shouldn't be stripped
-keep class androidx.compose.** { *; }
-dontwarn androidx.compose.**
# ----------- KEEP Hilt / Dagger -----------
-keep class dagger.hilt.** { *; }
-keep class javax.inject.** { *; }
-keep class dagger.internal.** { *; }
-dontwarn dagger.hilt.**
-dontwarn javax.inject.**
# Keep generated Hilt classes
-keep class * extends dagger.hilt.internal.GeneratedComponent { *; }
-keep class * implements dagger.hilt.internal.GeneratedComponent { *; }
# Don't strip metadata used for dependency injection
-keepclassmembers class ** {
@dagger.** <fields>;
@dagger.** <methods>;
}
# ----------- KEEP Retrofit -----------
# Retrofit uses reflection to call APIs
-keep interface retrofit2.** { *; }
-keep class retrofit2.** { *; }
-dontwarn retrofit2.**
# Keep your Retrofit API interfaces
-keep class com.yourpackage.api.** { *; } # <-- Change to match your API package
# ----------- KEEP Gson Models -----------
# Gson uses reflection for serialization/deserialization
-keep class com.yourpackage.model.** { *; } # <-- Replace with your models package
-keepclassmembers class com.yourpackage.model.** {
<fields>;
}
# Optional: prevent Gson from removing field names
-keepattributes Signature
-keepattributes *Annotation*
# ----------- KEEP Room -----------
-keep class androidx.room.** { *; }
-dontwarn androidx.room.**
# Room uses annotations for entity/schema info
-keepclassmembers class * {
@androidx.room.* <methods>;
@androidx.room.* <fields>;
}
# ----------- AndroidX Core (Optional) -----------
-dontwarn androidx.**
-keep class androidx.** { *; }
# ----------- General Safe Settings -----------
-dontwarn kotlin.**
-dontwarn org.jetbrains.annotations.**
-keep class kotlin.Metadata { *; }
# ----------- Optional: Keep ViewModel / SavedState Handles -----------
-keep class com.yourpackage.viewmodel.** { *; } # <-- Replace with your ViewModel package
-keepclassmembers class * extends androidx.lifecycle.ViewModel {
<init>();
}Conclusion
Test your release build thoroughly after enabling minification. If anything crashes mysteriously, it’s likely a ProGuard/R8 issue — check logs for missing classes or methods and add rules accordingly.








