By default, R8 makes a lot of optimizations to improve performance and size, but the optimizations might not work for your app immediately. If you're turning on R8 (or enabling full mode) in a big app for the first time, try to adopt optimizations incrementally: temporarily turn off obfuscation and enable R8 for portions of code at a time, rather than for all the code in your app. We recommend taking this incremental approach during local development, but you can also use it during internal QA testing or even in production as a gradual rollout. The exact steps you take depend on your desired timeline and confidence in your pre-release testing coverage.
Limit the optimizations
R8 does many types of optimizations including removing code, rewriting code, and removing resources. Here are some high-level descriptions of the optimization types:
- Code shrinking (or tree shaking): removes unreferenced code
- Obfuscation (or identifier minification): shortens the names of classes and methods
- Optimization: rewrites code, for example inlining
To reduce the chance of errors, you can start by enabling only some of these optimizations.
Enable tree shaking only
Code shrinking, also known as tree shaking, removes code that appears to be unreferenced. We recommend starting with just tree shaking, since it's the most straightforward.
To enable tree shaking only, add the following to your
proguard-rules.pro
file to turn off the other types of optimizations. Turning
off obfuscation is key because it makes stack traces much easier to read.
-dontobfuscate // Use temporarily to turn off identifier minification
-dontoptimize // Use temporarily to turn off optimization
In the end, you wouldn't want to ship this configuration, since it drastically limits the ability for R8 to optimize code, but it's a great starting point when adopting R8 for the first time in a large codebase with problems to fix.
Use compat mode
By default, R8 runs in full mode. Full mode imparts significantly improved performance and size savings, but you can temporarily disable it and use compat mode instead when enabling minification for the first time.
To use compat mode, use the following setting in your gradle.properties
file:
android.enableR8.fullMode = false // Use temporarily to disable full mode
Enable the rest of the optimizations
When you've confirmed that tree shaking works for your app, you can remove the preceding settings to re-enable obfuscation, optimization, and R8 full mode. Note that obfuscation can make debugging more difficult, which is why we recommend addressing tree shaking issues first.
For more information about deobfuscating stack traces, see Recover the original stack trace.
Limit the optimization scope
A fully optimized build optimizes all code across every library and package, so it's common to encounter issues with R8 when you first turn it on. If you find a problem with optimization in one part of the app, don't turn off R8 completely or you'll lose out on benefits everywhere else. Instead, temporarily disable R8 only in the parts of your app that are causing problems.
Use package-wide keep rules
We recommend using package-wide keep rules as a way to temporarily disable R8 in parts of your app. You should always come back to fix these optimization issues later; this is generally a stop-gap solution to work around problem areas.
For example, if part of your app uses Gson heavily and is causing problems with
optimization, the ideal fix is to add more
targeted keep rules or move to a codegen solution. But
to unblock optimizing the rest of the app, you can place the code defining your
Gson types in a dedicated subpackage, and add a rule like this to your
proguard-rules.pro
file:
-keep class com.myapp.json.** { *; }
If some library you're using has reflection into internal components, you can similarly add a keep rule for the entire library. You'll need to inspect the library's code or JAR/AAR to find the appropriate package to keep. Again, this isn't recommended to maintain long term, but can unblock the optimization of the rest of the app:
-keep class com.somelibrary.** { *; }
Remove package-wide keep rules
Once your app functions correctly with package-wide keep rules, you should go back and either add targeted keep rules, or remove the reflection usage or library which necessitates the keep rule in the first place.
For example, keeping all rules which extend a certain class is extremely common in AndroidX to keep only relevant classes. Generally, reflection should only target classes or methods which either extend certain abstract classes, implement certain interfaces, or into classes with a specific runtime annotation. Each of these are supported ways to define keep rules so that you don't need package-wide keep rules in your final, fully optimized app.