blob: f3dde4ee073ffd362cff147a6f53679b85157abe [file] [log] [blame] [view]
Søren Gjessece1f4272019-02-28 10:28:49 +01001# R8 compatibility FAQ
2
3R8 uses the same configuration specification language as ProGuard, and tries to
4be compatible with ProGuard. However as R8 has different optimizations it can be
5necessary to change the configuration when switching to R8.
6
7This FAQ collects some of the common issues.
8
9## GSON
10
11### Member in a data object is always `null`
12
13For data classes used for serialization all fields that are used in the
14serialization must be kept by the configuration. R8 can decide to replace
15instances of types that are never instantiated with `null`. So if instances of a
16given class are only created through deserialization from JSON, R8 will not see
17that class as instantiated leaving it as always `null`.
18
19If the `@SerializedName` annotation is used consistently for data classes the
20following keep rule can be used:
21
22```
23-keepclassmembers,allowobfuscation class * {
24 @com.google.gson.annotations.SerializedName <fields>;
25}
26```
27
28This will ensure that all fields annotated with `SerializedName` will be
29kept. These fields can still be renamed during obfuscation as the
30`SerializedName` annotation (not the source field name) controls the name in the
31JSON serialization.
32
33If the `@SerializedName` annotation is _not_ used the following conservative
34rule can be used for each data class:
35
36```
37-keepclassmembers class MyDataClass {
38 !transient <fields>;
39}
40```
41
42This will ensure that all fields are kept and not renamed for these
43classes. Fields with modifier `transient` are never serialized and therefore
44keeping these is not needed.
45
46### Error `java.lang.IllegalArgumentException: class <class name> declares multiple JSON fields named <name>`
47
48This can be caused by obfuscation selecting the same name for private fields in
49several classes in a class hierachy. Consider the following example:
50
51```
52class A {
53 private String fieldInA;
54}
55
56class B extends A {
57 private String fieldInB;
58}
59```
60
61Here R8 can choose to rename both `fieldInA` and `fieldInB` to the same name,
62e.g. `a`. This creates a conflict when GSON is used to either serialize an
63instance of class `B` to JSON or create an instance of class `B` from JSON. If
64the fields should _not_ be serialized they should be marked `transient` so that
65they will be ignored by GSON:
66
67```
68class A {
69 private transient String fieldInA;
70}
71
72class B extends A {
73 private transient String fieldInB;
74}
75```
76
77If the fields _are_ to be serialized, the annotation `SerializedName` can be
78used to fix the `IllegalArgumentException` together the rule to keep fields
79annotated with `SerializedName`
80
81```
82class A {
83 @SerializedName("fieldInA")
84 private String fieldInA;
85}
86
87class B extends A {
88 @SerializedName("fieldInB")
89 private String fieldInB;
90}
91```
92
93```
94-keepclassmembers,allowobfuscation class * {
95 @com.google.gson.annotations.SerializedName <fields>;
96}
97```
98
99
100Both the use of `transient` and the use of the annotation `SerializedName` allow
101the fields to be renamed by R8 to the same name, but GSON serialization will
102work as expected.
103
Christoffer Quist Adamsend96a2cf2019-03-07 09:58:51 +0100104# R8 full mode
105
106In full mode, R8 performs more aggressive optimizations, meaning that additional
107ProGuard configuration rules may be required. This section highlights some
108common issues that have been seen when using full mode.
109
110## Retrofit
111
112### Object instantiated with Retrofit's `create()` method is always replaced with `null`
113
114This happens because Retrofit uses reflection to return an object that
115implements a given interface. The issue can be resolved by using the most recent
116keep rules from the Retrofit library.
117
118See also https://github.com/square/retrofit/issues/3005 ("Insufficient keep
119rules for R8 in full mode").