MyFamilyTracker
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
R8 is enabled in every release build but most developers just hope it works. When it breaks something, debugging is painful. Here's how to configure ProGuard rules correctly, debug obfuscation issues, and keep your app running after shrinking.
On this page
R8 is one of those things that works silently until it doesn't. You ship a release build, something crashes, and the stack trace is full of single-letter class and method names. Here's how to prevent and fix these problems.
R8 performs three operations in one pass:
TaskRepositoryaAll three happen automatically in release builds when
isMinifyEnabled = trueR8 analyzes code statically — it looks at what's called in code it can see. It can't see:
Class.forName("com.yourapp.SomeClass")SomeClassObfuscated crashes look like:
java.lang.NullPointerException
at a.b.c.d(Unknown Source:12)
at e.f.g.h(Unknown Source:5)To de-obfuscate:
# Using the retrace tool
java -jar retrace.jar \
app/build/outputs/mapping/release/mapping.txt \
stacktrace.txtOr in Android Studio: Analyze → Stack Trace... → paste the obfuscated trace → click "Deobfuscate."
Always save mapping files. The mapping file connects obfuscated names to original names. R8 generates it at
app/build/outputs/mapping/release/mapping.txtThe
proguard-rules.pro# Keep the full class name
-keep class com.yourapp.data.models.** { *; }
# Keep specific class
-keep class com.yourapp.SomeClass { *; }
# Keep class members (fields and methods)
-keepclassmembers class com.yourapp.SomeClass {
public <fields>;
public <methods>;
}For Kotlinx Serialization:
-keepattributes *Annotation*
-keepclassmembers @kotlinx.serialization.Serializable class ** {
*** Companion;
kotlinx.serialization.KSerializer serializer(...);
}For Gson:
-keepclassmembers class com.yourapp.data.dto.** {
<fields>;
}-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
}Annotate specific classes or methods instead of writing rules:
@Keep
data class TaskDto(
val id: String,
val title: String,
val completed: Boolean
)
class TaskRepository {
@Keep
fun getTask(id: String): Task? { ... }
}@KeepWhen a release build crashes but debug doesn't:
Step 1: Run the release build with
minifyEnabled = truedebuggable = truebuildTypes {
create("releaseDebug") {
initWith(getByName("play store"))
isDebuggable = true
applicationIdSuffix = ".releasedebug"
}
}Step 2: Use R8's
-printusage-whyareyoukeeping# In proguard-rules.pro (temporarily, for debugging)
-printusage build/r8-usage.txt
-whyareyoukeeping class com.yourapp.MyClassStep 3: Binary search. Disable obfuscation first:
// Temporarily disable obfuscation (keep shrinking)
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)Add to proguard-rules.pro:
-dontobfuscateIf the crash goes away, the issue is obfuscation (field/class name change). If it persists, the issue is shrinking (class or method removed).
Good libraries ship with their own ProGuard rules that R8 applies automatically (via
proguard.txtLibraries that require manual rules in your
proguard-rules.pro./gradlew assembleRelease
# View what R8 removed
cat app/build/outputs/mapping/release/usage.txt | head -100The
usage.txt# Keep Kotlin metadata
-keepattributes *Annotation*
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
# Keep enum members
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# Keep Parcelable implementations
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# Keep serializable classes
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}@KeepSudarshan Chaudhari
AI Systems Builder / Product Engineer
Bangkok, Thailand
Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.
Related Posts
Related Apps
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Building something? Available for Android dev and QA consulting.
Work with meComments — powered by Giscus