blob: 3ba23c811e11fffadabe26628b87c9061f9a29de [file] [log] [blame] [view]
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +02001# R8 FAQ
Søren Gjessece1f4272019-02-28 10:28:49 +01002
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
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +02005necessary to change the configuration when switching to R8. R8 provides two
6modes, R8 compatibility mode and R8 full mode. R8 compatibility mode is default
7in Android Studio and is meant to make the transition to R8 from ProGuard easier
8by limiting the optimizations performed by R8.
Søren Gjessece1f4272019-02-28 10:28:49 +01009
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020010## R8 full mode
11In non-compat mode, also called full mode”, R8 performs more aggressive
12optimizations, meaning additional ProGuard configuration rules may be required.
13Full mode can be enabled by adding `android.enableR8.fullMode=true` in the
14`gradle.properties` file. The main differences compared to R8 compatibility mode
15are:
16
17- The default constructor (`<init>()`) is not implicitly kept when a class is
18kept.
19- The default constructor (`<init>()`) is not implicitly kept for types which
20are only used with `ldc`, `instanceof` or `checkcast`.
21- The enclosing classes of fields or methods that are matched by a
22`-keepclassmembers` rule are not implicitly considered to be instantiated.
23Classes that are only instantiated using reflection should be kept explicitly
24with a `-keep` rule.
25- Default methods are not implicitly kept as abstract methods.
26- Attributes (such as `Signature`) and annotations are only kept for classes,
27methods and fields which are matched by keep rules even when `-keepattributes`
28is specified.
29Additionally, for attributes describing a relationship such as `InnerClass` and
30`EnclosingMethod`, non-compat mode requires both endpoints being kept.
31
32# Troubleshooting
33
34The rest of this document describes known issues with libraries that use
35reflection.
Søren Gjessece1f4272019-02-28 10:28:49 +010036
37## GSON
38
39### Member in a data object is always `null`
40
41For data classes used for serialization all fields that are used in the
42serialization must be kept by the configuration. R8 can decide to replace
43instances of types that are never instantiated with `null`. So if instances of a
44given class are only created through deserialization from JSON, R8 will not see
45that class as instantiated leaving it as always `null`.
46
47If the `@SerializedName` annotation is used consistently for data classes the
48following keep rule can be used:
49
50```
51-keepclassmembers,allowobfuscation class * {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020052 @com.google.gson.annotations.SerializedName <fields>;
Søren Gjessece1f4272019-02-28 10:28:49 +010053}
54```
55
56This will ensure that all fields annotated with `SerializedName` will be
57kept. These fields can still be renamed during obfuscation as the
58`SerializedName` annotation (not the source field name) controls the name in the
59JSON serialization.
60
61If the `@SerializedName` annotation is _not_ used the following conservative
62rule can be used for each data class:
63
64```
65-keepclassmembers class MyDataClass {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020066 !transient <fields>;
Søren Gjessece1f4272019-02-28 10:28:49 +010067}
68```
69
70This will ensure that all fields are kept and not renamed for these
71classes. Fields with modifier `transient` are never serialized and therefore
72keeping these is not needed.
73
74### Error `java.lang.IllegalArgumentException: class <class name> declares multiple JSON fields named <name>`
75
76This can be caused by obfuscation selecting the same name for private fields in
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020077several classes in a class hierarchy. Consider the following example:
Søren Gjessece1f4272019-02-28 10:28:49 +010078
79```
80class A {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020081 private String fieldInA;
Søren Gjessece1f4272019-02-28 10:28:49 +010082}
83
84class B extends A {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020085 private String fieldInB;
Søren Gjessece1f4272019-02-28 10:28:49 +010086}
87```
88
89Here R8 can choose to rename both `fieldInA` and `fieldInB` to the same name,
90e.g. `a`. This creates a conflict when GSON is used to either serialize an
91instance of class `B` to JSON or create an instance of class `B` from JSON. If
92the fields should _not_ be serialized they should be marked `transient` so that
93they will be ignored by GSON:
94
95```
96class A {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020097 private transient String fieldInA;
Søren Gjessece1f4272019-02-28 10:28:49 +010098}
99
100class B extends A {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200101 private transient String fieldInB;
Søren Gjessece1f4272019-02-28 10:28:49 +0100102}
103```
104
105If the fields _are_ to be serialized, the annotation `SerializedName` can be
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200106used to fix the `IllegalArgumentException` together with the rule to keep fields
Søren Gjessece1f4272019-02-28 10:28:49 +0100107annotated with `SerializedName`
108
109```
110class A {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200111 @SerializedName("fieldInA")
112 private String fieldInA;
Søren Gjessece1f4272019-02-28 10:28:49 +0100113}
114
115class B extends A {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200116 @SerializedName("fieldInB")
117 private String fieldInB;
Søren Gjessece1f4272019-02-28 10:28:49 +0100118}
119```
120
121```
122-keepclassmembers,allowobfuscation class * {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200123 @com.google.gson.annotations.SerializedName <fields>;
Søren Gjessece1f4272019-02-28 10:28:49 +0100124}
125```
126
127
128Both the use of `transient` and the use of the annotation `SerializedName` allow
129the fields to be renamed by R8 to the same name, but GSON serialization will
130work as expected.
131
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200132### GSON with full mode
Christoffer Quist Adamsend96a2cf2019-03-07 09:58:51 +0100133
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200134GSON uses type tokens to serialize and deserialize generic types.
135
136```TypeToken<List<String>> listOfStrings = new TypeToken<List<String>>() {};```
137
138The anonymous class will have a generic signature argument of `List<String>` to
139the super type `TypeToken` that is reflective read for serialization. It
140is therefore necessary to keep both the `Signature` attribute, the
141`com.google.gson.reflect.TypeToken` class and all sub-types:
142
143```
144-keepattributes Signature
145-keep class com.google.gson.reflect.TypeToken { *; }
146-keep class * extends com.google.gson.reflect.TypeToken
147```
Christoffer Quist Adamsend96a2cf2019-03-07 09:58:51 +0100148
149## Retrofit
150
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200151### Objects instantiated with Retrofit's `create()` method are always replaced with `null`
Christoffer Quist Adamsend96a2cf2019-03-07 09:58:51 +0100152
153This happens because Retrofit uses reflection to return an object that
154implements a given interface. The issue can be resolved by using the most recent
155keep rules from the Retrofit library.
156
157See also https://github.com/square/retrofit/issues/3005 ("Insufficient keep
158rules for R8 in full mode").
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200159
160### Kotlin suspend functions and generic signatures
161
162For Kotlin suspend functions the generic signature is reflectively read.
163Therefore keeping the `Signature` attribute is necessary. Full mode only keeps
164the signature for kept classes thus a keep on `kotlin.coroutines.Continuation` in
165addition to a keep on the api classes is needed:
166```
167-keepattributes Signature
168-keep class kotlin.coroutines.Continuation
169```
170
171This should be included automatically from versions built after the pull-request
172https://github.com/square/retrofit/pull/3563
173