For Android apps built with R8, exclude only the classes and members that the runtime accesses in ways R8 cannot see—such as JNI callbacks, reflection lookups, or serialization—and preserve only the behavior those contracts require. A rule may prevent removal, renaming, or both, so identify the needed protection rather than keeping whole packages by default.
How to decide what needs a keep rule
Ordinary code reached through visible Java or Kotlin call sites generally does not need a keep rule simply because it is part of the app. Start with a dynamic or external access path, then pinpoint the element it uses and the exact behavior that must remain. Android’s keep-rules overview describes the rule structure as a class or implementation plus the members to preserve.
As an Amazon Associate I earn from qualifying purchases.
- Identify the access mechanism. Look for native JNI calls into managed code, reflection or string-based lookup, serialization/deserialization, or another framework contract not represented by ordinary call sites.
- Identify the precise target. It may be a class, constructor, field, method, annotation, or a type appearing in a method descriptor. For a JNI callback, the callback signature’s types can matter as well as the method.
- Choose the required protection. Decide separately whether R8 must retain the code, preserve its name, or both. Do not assume every rule containing “keep” has identical effects.
- Check dependency rules and build mode. A library may supply consumer rules, and full mode can expose assumptions that worked in compatibility mode. Check the project’s R8/Android Gradle Plugin configuration before adopting an example.
- Inspect the built result and exercise the dynamic path. Use mapping and rule diagnostics to check matches, removals, retention reasons, and renamed symbols; then verify the runtime behavior that relies on them.
What the keep-rule options actually protect
Android documents six options: -keep, -keepclassmembers, -keepclasseswithmembers, -keepnames, -keepclassmembernames, and -keepclasseswithmembernames. Their effects differ. For example, -keepclassmembers applies to specified members only while the containing class remains; by itself, it does not ensure that an otherwise removable class stays. A name-preserving option can also allow shrinking, so an unchanged name does not necessarily mean the element survives.
Bare -keep can restrict optimization as well as removal and renaming. Prefer the narrowest matching rule and option that satisfy the runtime contract; broad rules can inhibit shrinking or optimization and make it harder to understand why code remains. See Android’s keep-rule option descriptions when adapting a pattern.
#1 Best Overall
When to preserve specific classes or members
JNI callbacks from native code
When C or C++ code calls Java or Kotlin methods through JNI, R8 cannot see those native call sites. Android warns that an unreferenced managed callback can therefore be removed. Target the bridge callback and, where the boundary requires stable descriptor types, those types too. Android’s JNI example uses -keepclassmembers,includedescriptorclasses for a bridge callback and a separate constructor rule for its data object:
-keepclassmembers,includedescriptorclasses class com.example.JniBridge {
public void onNativeEvent(com.example.Event);
}
-keep class com.example.Event {
public <init>();
}
Replace the package, class, method, visibility, and signature with the app’s actual JNI contract; this is a pattern, not a universal rule. If practical, keep JNI bridge code in an isolated package so rules can target that integration surface. Also distinguish native-to-managed callbacks from Java/Kotlin methods that call native methods: Android says the default proguard-android-optimize.txt includes a rule guarding native methods against trimming, while callbacks from native code need their own targeted preservation.
Rank #2
Reflection and string-based lookup
Reflection can instantiate a class or locate a method, field, or class by a name that has no ordinary call site. Preserve the elements the lookup actually needs. If the library uses a class name or member name as a string, renaming may break lookup; if it only needs an element to exist, shrinking protection may be enough. Check whether the library already provides consumer rules before adding app-level rules.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Serialization and Gson models
Serialization rules depend on how the particular library discovers types and fields. Android’s keep-rule examples include conditional rules for Gson fields annotated with @SerializedName. The R8 8.2.22 FAQ explains that consistently annotated fields can still be renamed when serialization uses the annotation’s JSON name rather than the Java field name. That behavior is specific to the library and configuration: do not infer that unannotated models or other JSON libraries need no protection.
Constructors and reflection-only classes in full mode
The R8 FAQ pinned to version 8.2.22 says full mode does not automatically retain a default constructor just because a class is kept. A class instantiated only by reflection must be explicitly preserved as needed, including its constructor. The same FAQ describes annotation and attribute retention as applying only to matched elements under the stated full-mode conditions, even when -keepattributes is present. Verify the exact R8 version and mode before relying on these details; do not assume that a class rule implicitly preserves every constructor or piece of metadata.
Diagnose overly broad or ineffective rules
The ProGuard configuration language is also used by R8, which aims for compatibility, but inspect the results rather than relying on a rule’s appearance. The Android Open Source Project’s ProGuard usage manual documents these diagnostic options:
-printseedsreports elements matched by keep rules.-printusagereports removed code.-whyareyoukeepinghelps explain why an element is retained.-printmappingwrites the mapping of original names to obfuscated names.
Use the reports to confirm that a narrow rule matches the intended element and that names or code are preserved only where the runtime contract needs them. A rule matching nothing will not protect the dynamic path; an unnecessarily broad rule may preserve much more than that path requires.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Quick Recap
Best Value
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




