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 →When Gson fields become null, disappear from JSON, or fail only in a minified Android release, the cause may be more than renamed model fields. R8 can remove code it cannot see being used, rename identifiers that reflection expects, or strip constructors and metadata needed at runtime. The reliable fix is to identify the dynamic access, preserve only what it needs, and test the minified artifact. For JSON contracts, use explicit names such as Gson’s @SerializedName rather than relying on source field names.
What R8 or ProGuard can break
R8 shrinks, optimizes, and obfuscates Android code. Direct calls are visible to static analysis; reflective calls often are not. A class created from a string, a constructor located at runtime, a member found by name, or fields scanned by a serializer can be a runtime dependency without an ordinary code reference. Shrinking may remove something that appears unused, while obfuscation may rename something a string-based lookup expects to find.
As an Amazon Associate I earn from qualifying purchases.
R8’s keep-rules documentation illustrates conditional rules for reflective patterns. The rule should describe the actual runtime dependency rather than indiscriminately preserving an entire package.
Typical symptoms and what they suggest
- Fields deserialize as null or vanish from output: check field retention, serialized names, and whether the release rules differ from debug.
- Construction fails or defaults change: check whether the runtime expects a no-argument constructor and whether it survives shrinking. In R8 full mode, a default constructor is not automatically kept just because its class is kept.
- Generic types resolve incorrectly: check whether generic signature metadata is retained where the library needs it, especially for Gson
TypeTokenuse. - Reflection cannot find a class or member: check string-based names and whether the relevant type or member was renamed or removed.
- A hierarchy triggers duplicate JSON field-name errors: inspect fields declared in both a superclass and subclass and the names Gson assigns after obfuscation.
These symptoms have different causes. “ProGuard renamed my model” is not a sufficient diagnosis: the model shape, constructor, annotations, superclass fields, metadata, and release configuration all matter.
#1 Best Overall
Keep JSON names stable without keeping source names
A JSON property name is part of the data contract when payloads are stored or exchanged. It should not depend on a Java field’s current identifier. Gson’s @SerializedName gives a field an explicit wire name, so the source field can be renamed while the JSON property remains stable. Android’s keep-rules guidance notes that Gson-annotated fields can be kept by suitable rules; the R8 FAQ also explains that the annotation value controls the JSON name even if the Java field is obfuscated.
That distinction is useful: preserve the name only when runtime code looks it up by its original name. For serialization, specify the external name explicitly instead of retaining source identifiers solely to make JSON stable.
Rank #2
Choose what to keep, constrain, or exclude
Start by naming the dynamic access: a class-name string, member-name string, reflective constructor, annotation scan, Gson field reflection, generic TypeToken, or a library bridge. Then identify exactly which element and metadata that path needs. A kept field alone may not be enough: full-mode behavior can depend on the class, constructor, annotations, and generic signature.
| Approach | Name stability | Reflection surface | Trade-off |
|---|---|---|---|
| Targeted keep rules | Preserves original names only for matched elements when the rule requires it | Retains only the classes, members, constructors, or attributes the rule covers | Allows more optimization than broad retention, but rules must match real runtime use |
| Constrained Gson reflection | Use @SerializedName for stable JSON names; source names can vary |
Reflection remains, but models are more explicit and predictable | Requires suitable model structure and minified-build testing |
| Explicit Gson adapters or JSON APIs | Names and conversion behavior are defined in code | Avoids relying on open-ended model reflection for handled types | Requires adapters or explicit readers/writers for the types involved |
Use narrow keep rules
Write rules around the class or member that reflection actually reaches. Android recommends conditional keep rules for cases where a reflective member is needed only if a matching class or member exists. This can preserve a reflective bridge without keeping unrelated code. Avoid copying a broad legacy rule without checking the current R8 version, library consumer rules, and runtime behavior.
R8’s FAQ for version 8.2.22 says full mode is more aggressive than compatibility mode and can require additional rules. In that mode, reflected-only classes need explicit keeping, and annotations or attributes are retained only for program elements matched by keep rules. A rule that keeps one field does not automatically guarantee the constructor or Signature metadata needed elsewhere.
Constrain Gson reflection or replace it for critical types
Gson’s current Android troubleshooting guidance says Gson is not recommended when minifying because open-ended reflection can be difficult to support, even with rules bundled since Gson 2.11.0. Its suggested constraints include providing no-argument constructors, making model classes top-level or static so they do not acquire implicit constructor parameters, and annotating serialized fields with @SerializedName. The alternative is to define TypeAdapter or TypeAdapterFactory implementations, or use Gson’s explicit JSON tree APIs and manual readers or writers.
Gson ships consumer rules, but its bundled gson.pro file says application-specific rules may still be needed, including rules for particular fields or no-argument constructors. Treat those bundled rules as a starting point, not a guarantee for every model and reflective use.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsExclude fields that are not part of the data contract
If a field should not be serialized, mark it transient when that matches the intended behavior. Excluding an internal or derived field is different from keeping a field to fix a release failure: first decide whether the value belongs in the JSON representation. For fields that should be serialized across a class hierarchy, assign distinct explicit names where necessary to avoid collisions.
Test the minified release artifact
A debug build cannot establish that reflection-based serialization works after shrinking and obfuscation. Gson’s troubleshooting guidance explicitly calls for testing after minification. Exercise the actual release variant and use representative payloads, including older stored or received JSON if compatibility matters.
- Verify serialized property names and values, and deserialize representative inputs back into the expected model.
- Check constructor behavior, field defaults, generic collections or wrappers using
TypeToken, and polymorphic cases. - Include models with inherited fields and confirm that their JSON names remain distinct.
- Run with the same R8 mode, consumer rules, and release configuration used for delivery.
When a failure occurs, inspect the generated mapping file to see which names changed; mapping information can also support retracing obfuscated stack traces. Use the result to refine the rule or model design, then rerun the release tests. Do not treat success in debug as evidence that the minified build is safe.
Why the exact R8 version matters
The full-mode details above are documented in the R8 FAQ tagged 8.2.22, not a claim that every Android project uses that exact version or mode. Check the R8 version, whether full or compatibility mode is active, and which rules arrive through dependencies before changing configuration. The runtime library’s needs determine whether to preserve a name, constructor, annotation, or generic signature.
The same reasoning applies to other reflection-driven libraries: identify what they discover dynamically and consult that library’s documentation for its own annotations and keep rules. Gson-specific annotations and configuration are not universal solutions.
Quick Recap
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.




