| # Optimizing Java Code | |
| This doc describes how Java code is optimized in Chrome on Android and how to | |
| deal with issues caused by the optimizer. For tips on how to write optimized | |
| code, see [//docs/speed/binary_size/optimization_advice.md#optimizing-java-code](/docs/speed/binary_size/optimization_advice.md#optimizing-java-code). | |
| [TOC] | |
| ## ProGuard vs R8 | |
| ProGuard is the original open-source tool used by many Android applications to | |
| perform whole-program bytecode optimization. [R8](https://r8.googlesource.com/r8), | |
| is a re-implementation that is used by Chrome (and the default for Android Studio). | |
| The terms "ProGuard" and "R8" are used interchangeably within Chromium but | |
| generally they're meant to refer to the tool providing Java code optimizations. | |
| ## What does ProGuard do? | |
| 1. Shrinking: ProGuard will remove unused code. This is especially useful | |
| when depending on third party libraries where only a few functions are used. | |
| 2. Obfuscation: ProGuard will rename classes/fields/methods to use shorter | |
| names. Obfuscation is used for minification purposes only (not security). | |
| 3. Optimization: ProGuard performs a series of optimizations to shrink code | |
| further through various approaches (ex. inlining, outlining, class merging, | |
| etc). | |
| ## Build Process | |
| ProGuard is enabled only for release builds of Chrome because it is a slow build | |
| step and breaks Java debugging. It can also be enabled manually via the GN arg: | |
| ```is_java_debug = false``` | |
| ### ProGuard configuration files | |
| Most GN Java targets can specify ProGuard configuration files by setting the | |
| `proguard_configs` variable. [//base/android/proguard](/base/android/proguard) | |
| contains common flags shared by most Chrome applications. | |
| ### GN build rules | |
| When `is_java_debug = false` and a target has enabled ProGuard, the `proguard` | |
| step generates the `.dex` files for the application. The `proguard` step takes | |
| as input a list of `.jar` files, runs R8/ProGuard on those `.jar` files, and | |
| produces the final `.dex` file(s) that will be packaged into your `.apk` | |
| ## Deobfuscation | |
| Obfuscation can be turned off for local builds while leaving ProGuard enabled | |
| by setting `enable_proguard_obfuscation = false` in GN args. | |
| There are two main methods for deobfuscating Java stack traces locally: | |
| 1. Using APK wrapper scripts (stacks are automatically deobfuscated) | |
| * `$OUT/bin/chrome_public_apk logcat` # Run adb logcat | |
| * `$OUT/bin/chrome_public_apk run` # Launch chrome and run adb logcat | |
| 2. Using `java_deobfuscate` | |
| * build/android/stacktrace/java_deobfuscate.py $OUT/apks/ChromePublic.apk.mapping < logcat.txt` | |
| * ProGuard mapping files are located beside APKs (ex. | |
| `$OUT/apks/ChromePublic.apk` and `$OUT/apks/ChromePublic.apk.mapping`) | |
| Helpful links for deobfuscation: | |
| * [Internal bits about how mapping files are archived][proguard-site] | |
| * [More detailed deobfuscation instructions][proguard-doc] | |
| * [Script for deobfuscating official builds][deob-official] | |
| [proguard-site]: http://goto.google.com/chrome-android-proguard | |
| [proguard-doc]: http://goto.google.com/chromejavadeobfuscation | |
| [deob-official]: http://goto.google.com/chrome-android-official-deobfuscation | |
| ## Debugging common failures | |
| ProGuard failures are often hard to debug. This section aims to outline some of | |
| the more common errors. | |
| ### Classes expected to be discarded | |
| The `-checkdiscard` directive can be used to ensure that certain items are | |
| removed by ProGuard. A common use of `-checkdiscard` it to ensure that ProGuard | |
| optimizations do not regress in their ability to remove code, such as code | |
| intended only for debug builds, or generated JNI classes that are meant to be | |
| zero-overhead abstractions. Annotating a class with | |
| [@CheckDiscard][checkdiscard] will add a `-checkdiscard` rule automatically. | |
| [checkdiscard]: /base/android/java/src/org/chromium/base/annotations/CheckDiscard.java | |
| ``` | |
| Item void org.chromium.base.library_loader.LibraryPrefetcherJni.<init>() was not discarded. | |
| void org.chromium.base.library_loader.LibraryPrefetcherJni.<init>() | |
| |- is invoked from: | |
| | void org.chromium.base.library_loader.LibraryPrefetcher.asyncPrefetchLibrariesToMemory() | |
| ... more code path lines | |
| |- is referenced in keep rule: | |
| | obj/chrome/android/chrome_public_apk/chrome_public_apk.resources.proguard.txt:104:1 | |
| Error: Discard checks failed. | |
| ``` | |
| Things to check | |
| * Did you add code that is referenced by code path in the error message? | |
| * If so, check the original class for why the `CheckDiscard` was added | |
| originally and verify that the reason is still valid with your change (may | |
| need git blame to do this). | |
| * Try the extra debugging steps listed in the JNI section below. | |
| ### JNI wrapper classes not discarded | |
| Proxy native methods (`@NativeMethods`) use generated wrapper classes to provide | |
| access to native methods. We rely on ProGuard to fully optimize the generated | |
| code so that native methods aren't a source of binary size bloat. The above | |
| error message is an example when a JNI wrapper class wasn't discarded (notice | |
| the name of the offending class). | |
| * The ProGuard rule pointed to in the error message isn't helpful (just tells | |
| us a code path that reaches the not-inlined class). | |
| * Common causes: | |
| * Caching the result of `ClassNameJni.get()` in a member variable. | |
| * Passing a native wrapper method reference instead of using a lambda (i.e. | |
| `Jni.get()::methodName` vs. `() -> Jni.get.methodName()`). | |
| * For more debugging info, add to `base/android/proguard/chromium_code.flags`: | |
| ``` | |
| -whyareyounotinlining class org.chromium.base.library_loader.LibraryPrefetcherJni { | |
| <init>(); | |
| } | |
| ``` | |
| ### Duplicate classes | |
| ``` | |
| Type YourClassName is defined multiple times: obj/jar1.jar:YourClassName.class, obj/jar2.jar:YourClassName.class | |
| ``` | |
| Common causes: | |
| * Multiple targets with overlapping `srcjar_deps`: | |
| * Each `.srcjar` can only be depended on by a single Java target in any | |
| given APK target. `srcjar_deps` are just a convenient way to depend on | |
| generated files and should be treated like source files rather than | |
| `deps`. | |
| * Solution: Wrap the `srcjar` in an `android_library` target or have only a | |
| single Java target depend on the `srcjar` and have other targets depend on | |
| the containing Java target instead. | |
| * Accidentally enabling APK level generated files for multiple targets that | |
| share generated code (ex. Trichrome or App Bundles): | |
| * Solution: Make sure the generated file is only added once. | |
| Debugging ProGuard failures isn't easy, so please message java@chromium.org | |
| or [file a bug](crbug.com/new) with `component=Build os=Android` for any | |
| issues related to Java code optimization. |