blob: 37109c4c574f311d809607873165fe56e400f7ad [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`
Morten Krogh-Jespersen04752e12022-10-11 12:55:57 +020028is specified. The weakest rule that will keep annotations and attributes is
29`-keep[classmembers],allowshrinking,allowoptimization,allowobfuscation,allowaccessmodification class-specification`
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020030Additionally, for attributes describing a relationship such as `InnerClass` and
31`EnclosingMethod`, non-compat mode requires both endpoints being kept.
Søren Gjesse7a177c72024-04-03 15:23:52 +020032- When optimizing or minifying the `SourceFile` attribute will always be
33rewritten to `SourceFile` unless `-renamesourcefileattribute` is used in which
34case the provided value is used. The original source file name is in the mapping
35file and when optimizing or minifying a mapping file is always produced.
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020036
Ian Zerny42b4baf2023-04-26 11:55:47 +020037## Stack traces and retracing
38When compiling with R8, a mapping file can be produced to support mapping stack
39traces of the R8 optimized program back to the original source information.
40
41Starting with R8 version 8.2, the compiler will retain full original source file
42names in the mapping information without the need to specify
43`-keepattributes SourceFile`.
44
45In addition, builds that target API level 26 or above (and don't specify custom
46source-file information) will also retain mapping information for all lines
47without the need to specify `-keepattributes LineNumberTable`.
48
49More information on R8 mapping files can be found in the [retrace doc](doc/retrace.md).
50
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020051# Troubleshooting
52
53The rest of this document describes known issues with libraries that use
54reflection.
Søren Gjessece1f4272019-02-28 10:28:49 +010055
56## GSON
57
58### Member in a data object is always `null`
59
60For data classes used for serialization all fields that are used in the
61serialization must be kept by the configuration. R8 can decide to replace
62instances of types that are never instantiated with `null`. So if instances of a
63given class are only created through deserialization from JSON, R8 will not see
64that class as instantiated leaving it as always `null`.
65
66If the `@SerializedName` annotation is used consistently for data classes the
67following keep rule can be used:
68
69```
70-keepclassmembers,allowobfuscation class * {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020071 @com.google.gson.annotations.SerializedName <fields>;
Søren Gjessece1f4272019-02-28 10:28:49 +010072}
73```
74
75This will ensure that all fields annotated with `SerializedName` will be
76kept. These fields can still be renamed during obfuscation as the
77`SerializedName` annotation (not the source field name) controls the name in the
78JSON serialization.
79
80If the `@SerializedName` annotation is _not_ used the following conservative
81rule can be used for each data class:
82
83```
84-keepclassmembers class MyDataClass {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020085 !transient <fields>;
Søren Gjessece1f4272019-02-28 10:28:49 +010086}
87```
88
89This will ensure that all fields are kept and not renamed for these
90classes. Fields with modifier `transient` are never serialized and therefore
91keeping these is not needed.
92
93### Error `java.lang.IllegalArgumentException: class <class name> declares multiple JSON fields named <name>`
94
95This can be caused by obfuscation selecting the same name for private fields in
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +020096several classes in a class hierarchy. Consider the following example:
Søren Gjessece1f4272019-02-28 10:28:49 +010097
98```
99class A {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200100 private String fieldInA;
Søren Gjessece1f4272019-02-28 10:28:49 +0100101}
102
103class B extends A {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200104 private String fieldInB;
Søren Gjessece1f4272019-02-28 10:28:49 +0100105}
106```
107
108Here R8 can choose to rename both `fieldInA` and `fieldInB` to the same name,
109e.g. `a`. This creates a conflict when GSON is used to either serialize an
110instance of class `B` to JSON or create an instance of class `B` from JSON. If
111the fields should _not_ be serialized they should be marked `transient` so that
112they will be ignored by GSON:
113
114```
115class A {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200116 private transient String fieldInA;
Søren Gjessece1f4272019-02-28 10:28:49 +0100117}
118
119class B extends A {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200120 private transient String fieldInB;
Søren Gjessece1f4272019-02-28 10:28:49 +0100121}
122```
123
124If the fields _are_ to be serialized, the annotation `SerializedName` can be
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200125used to fix the `IllegalArgumentException` together with the rule to keep fields
Søren Gjessece1f4272019-02-28 10:28:49 +0100126annotated with `SerializedName`
127
128```
129class A {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200130 @SerializedName("fieldInA")
131 private String fieldInA;
Søren Gjessece1f4272019-02-28 10:28:49 +0100132}
133
134class B extends A {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200135 @SerializedName("fieldInB")
136 private String fieldInB;
Søren Gjessece1f4272019-02-28 10:28:49 +0100137}
138```
139
140```
141-keepclassmembers,allowobfuscation class * {
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200142 @com.google.gson.annotations.SerializedName <fields>;
Søren Gjessece1f4272019-02-28 10:28:49 +0100143}
144```
145
146
147Both the use of `transient` and the use of the annotation `SerializedName` allow
148the fields to be renamed by R8 to the same name, but GSON serialization will
149work as expected.
150
Morten Krogh-Jespersen72d73102022-09-28 13:51:53 +0200151### GSON
Christoffer Quist Adamsend96a2cf2019-03-07 09:58:51 +0100152
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200153GSON uses type tokens to serialize and deserialize generic types.
154
155```TypeToken<List<String>> listOfStrings = new TypeToken<List<String>>() {};```
156
157The anonymous class will have a generic signature argument of `List<String>` to
158the super type `TypeToken` that is reflective read for serialization. It
159is therefore necessary to keep both the `Signature` attribute, the
Morten Krogh-Jespersen72d73102022-09-28 13:51:53 +0200160`com.google.gson.reflect.TypeToken` class and all sub-types.
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200161
162```
163-keepattributes Signature
164-keep class com.google.gson.reflect.TypeToken { *; }
165-keep class * extends com.google.gson.reflect.TypeToken
166```
Christoffer Quist Adamsend96a2cf2019-03-07 09:58:51 +0100167
Morten Krogh-Jespersen72d73102022-09-28 13:51:53 +0200168This is also needed for R8 in compat mode since multiple optimizations will
169remove the generic signature such as class merging and argument removal.
170
Christoffer Quist Adamsend96a2cf2019-03-07 09:58:51 +0100171## Retrofit
172
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200173### Objects instantiated with Retrofit's `create()` method are always replaced with `null`
Christoffer Quist Adamsend96a2cf2019-03-07 09:58:51 +0100174
175This happens because Retrofit uses reflection to return an object that
176implements a given interface. The issue can be resolved by using the most recent
177keep rules from the Retrofit library.
178
179See also https://github.com/square/retrofit/issues/3005 ("Insufficient keep
180rules for R8 in full mode").
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200181
Morten Krogh-Jespersen573f37b2023-05-03 10:42:23 +0200182### Return type must be parameterized
183
184Consider a service as the following:
185```
186interface Api {
187
188 @GET("<uri>")
189 fun getData(): Observable<Data>
190
191}
192```
193
194Retrofit instantiate a return type by inspecting the generic signature, here
195`Observable` from `io.reactivex.rxjava3.core`. Those classes are not guarded by
196keep rules prior to https://github.com/square/retrofit/pull/3886 so one has to
197manually add keep rules to prevent R8 in full mode stripping the generic
198signature. The proposed rule in https://github.com/square/retrofit/pull/3886 is:
199```
200-if interface * { @retrofit2.http.* public *** *(...); }
201-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
202```
203After https://github.com/square/retrofit/pull/3886 is merged, the above rule
204is automatically included in your build. You can add the rule to your build
205until then.
206
207Note, the `Data` class probably also needs to be kept if used since this is
208also constructed reflectively.
209
Morten Krogh-Jespersen47229a42021-06-02 11:47:56 +0200210### Kotlin suspend functions and generic signatures
211
212For Kotlin suspend functions the generic signature is reflectively read.
213Therefore keeping the `Signature` attribute is necessary. Full mode only keeps
214the signature for kept classes thus a keep on `kotlin.coroutines.Continuation` in
215addition to a keep on the api classes is needed:
216```
217-keepattributes Signature
218-keep class kotlin.coroutines.Continuation
219```
220
221This should be included automatically from versions built after the pull-request
222https://github.com/square/retrofit/pull/3563
223