This document outlines common "bad" or redundant keep rules for standard Android development and popular libraries. Modern toolchains and libraries include their own consumer keep rules embedded in their AAR/JAR files, making many manual configurations unnecessary or even harmful to code optimization.
Case: Global Keep Rules
Common Mistakes:
proguard
-dontshrink
-dontobfuscate
-dontoptimize
The Fix: These keep rules completely disable the core optimizations of R8 for the entire codebase. They must be removed from the codebase.
Case: Android Components
Keep rules required for Android components like Activity, Fragment, ViewModel,
Views, Services or Broadcast receivers are redundant. AAPT2 and R8 contain the
logic to automatically keep components declared in the AndroidManifest.xml or
referenced in XML layout files.
Common Mistakes:
proguard
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Service
-keep public class * extends android.view.View
-keepclassmembers class * extends android.app.Fragment { public void *(android.view.View); }
The Fix: Delete these manual rules. AAPT2 handles this automatically.
Case: Official Android and Kotlin Libraries
Keep rules targeting official library packages like AndroidX, Kotlin, and Kotlinx are redundant as they are bundled within the libraries themselves. Manual rules are often broader than what is strictly needed.
Common Mistakes:
proguard
-keep class androidx.** { *; }
-keep class kotlinx.** { *; }
-keep class kotlin.** { *; }
The Fix: Delete these manual rules. Rely on the consumer keep rules packaged within these dependencies.
Case: Gson
Overly Broad Data Model Rules
The most common mistake is keeping entire packages of data models (POJOs/DTOs), keeping data models at all for deserialization is unnecessary.
-keep class com.example.app.models.** { *; }
-keep class com.example.app.package.models.* { *; }
Redundant Interface & Adapter Rules
These rules added for TypeAdapter are unnecessary and are already covered by the library, and prevent R8 from effectively shrinking and optimizing custom adapters. R8 can determine if the adapter implementation are used. Keeping them globally prevents the removal of unused adapter implementations.
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
Unnecessary TypeToken Rules
There is no need to handle generic type erasure, Gson's own rules handle the
necessary TypeToken preservation.
-keep class com.google.gson.reflect.TypeToken { *; }
-keep class * extends com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
Internal and Example Packages
Keeping internal library logic prevents the compiler from stripping away dead code within the library.
-keep class com.google.gson.internal.** { *; }
-keep class com.google.gson.internal.reflect.** { *; }
-keep class com.google.gson.internal.UnsafeAllocator { *; }
-keep class com.google.gson.stream.** { *; }
- Keeps Unused Code: Prevents R8 from removing models that are never actually used in the code.
- Prevents Method Stripping: Keeps all getters, setters,
toString(),equals(), andhashCode()methods, even if they are never called. - Blocks Obfuscation: Prevents the class names from being obfuscated,
which is unnecessary for Gson if you use
@SerializedName.
The Fix:
- Use
@SerializedNameon every field in your data classes uses so that the field is retained after R8 optimization - Modern Gson (v2.11.0+) bundles its own rules (View Gson's embedded
ProGuard
rules).
The bundled keep rules retains the
@SerializedNameannotated fields. If you are on an older version, move towards Gson version 2.11 because it has the necessary keep rules and delete the keep rules that target the classes used for gson serialization and deserialization
Case: Retrofit
Retrofit has shipped with its own consumer keep rules from 2.9.0 and higher, so any keep rules for the library or classes depending on Retrofit is detrimental to the optimization process.
Blanket Library Preservation
This is the most harmful Retrofit rule as it disables any shrinking for the entire library.
-keep class retrofit2.** { *; }
-keep class retrofit2.api.** { *; }
-keep class com.package.example.retrofit.api.** { *; }
Manual Annotation Keeps
Retrofit's consumer rules automatically keep the interfaces annotated with
@GET, @POST, @DELETE, @PUT, @HEAD, @OPTIONS, @PATCH, making these
manual rules obsolete.
-keepclasseswithmembers class * { @retrofit2.http.* <methods>; }
Redundant Network Response and Adapter Rules
Network responses and third-party adapter wrappers (like RxJava) are often overly preserved by developers out of caution.
-keep,allowobfuscation,allowshrinking class retrofit2.Response
-keep class retrofit2.adapter.rxjava2.Result { *; }
Fix: Verify you are using Retrofit 2.9.0 and higher. Retrofit from 2.9.0 bundles rules that detect its own HTTP annotations (@GET, @POST) (View Retrofit's embedded ProGuard rules). It will automatically keep the method signatures it needs to work.
Case: Kotlin Coroutines
Kotlin Coroutines comes heavily optimized out of the box with embedded R8 rules
(kotlinx-coroutines-core includes its own rules).
Blanket Coroutine Library Rules
Keeping everything under kotlinx.coroutines is extremely detrimental to app
size, as coroutines contain a vast amount of internal APIs that aren't used.
-keepclassmembers class kotlinx.coroutines.** { *; }
Redundant Internal Continuations
These low-level coroutine elements are preserved safely by the library's own consumer rules. Manually adding these prevents R8 from performing internal optimizations (such as removing unused continuations or inlining).
-keepclassmembers class kotlin.coroutines.SafeContinuation { *; }
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
Dispatcher and Exception Handler Rules
Sometimes developers notice crashes related to Missing Classes on old Android versions and add these rules, but if you are using an up-to-date version of Coroutines, these are handled automatically or are not an issue.
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
-keepnames class kotlinx.coroutines.android.AndroidExceptionPreHandler {}
-keepnames class kotlinx.coroutines.android.AndroidDispatcherFactory {}
Fix Remove any broad kotlinx keep rules. Coroutines (v1.7.0+) bundle
the necessary keep rules (View Coroutines' embedded ProGuard
rules).
Case: Parcelable
Common Mistakes: Legacy projects often contain -keep class * implements
android.os.Parcelable { public static final android.os.Parcelable$Creator *; }.
The Fix:
- Add the
kotlin-parcelizeplugin. - Use
@Parcelize: Replace manualwriteToParcellogic with the@Parcelizeannotation. - Delete All Parcelable Rules: The plugin automatically generates the required rules.
- The default proguard file
proguard-android-optimize.txtcontains the keep rules for keeping all the parcelable classes - Ideal Rule: None. Delete all manual Parcelable keeps.
Case: Room Database
Common Mistakes: Keeping DAO interfaces or the generated _Impl classes
manually.
-keep class * extends androidx.room.RoomDatabase
-keep class *_*Impl { *; }
The Fix: Room generates its own ProGuard rules for the code it creates. Manual rules are redundant and prevent R8 from optimizing the database access layers.
- Ideal Rule: None. Delete all manual Room or DAO keeps.
Summary
If you have updated your libraries to the versions mentioned, your
proguard-rules.pro must not contain any keep rules for the libraries
mentioned here.