R8: A Step in Google’s Android Build Performance Roadmap
Published on April 5, 2019 by Nathan Arthur
Google recently introduced R8, a new tool designed to replace ProGuard as the default shrinker in the Android build process. R8 is meant to produce as-good-or-better outputs than ProGuard, and to do so faster than ProGuard does, thereby reducing overall build times. It will be enabled by default in the next release of Android Gradle Plugin (v3.4).
This change removes a long-standing component (ProGuard) and replaces it with entirely-new code that does essentially the same thing. Why would Google do such an expensive, risky thing? We’ve been tracking the development of R8 since it first became public, and we have a pretty good idea.
Google is intensely focused on build performance, and R8 is just one step along a path leading toward a single-component toolchain that builds much faster than the current toolchain – possibly by removing traditional Java bytecode from the process altogether. R8, in itself, doesn’t make that big of a difference – builds “after” will be largely the same as builds “before”, albeit with incremental improvements to artifact size and build speed. The value of this step is that it enables Google to iterate even more on the build process – to make even bigger architectural changes – because they don’t depend on third-party components anymore.
To understand this, and to get some idea of where they’re going next, let’s look at the history of the Android build process.
Android builds have always been inherently complex because they start with Java source, compile that to Java bytecode, then convert that to Android dex. That bytecode stage isn’t fundamentally necessary; it’s possible to go straight to dex from Java source code. That two-stage conversion slows down the build, which slows down developers, which puts negative pressure on the entire ecosystem.
Google has demonstrated that they take that very seriously.
In 2014, Google announced the “experimental” Jack toolchain that was designed to replace the two-stage build process with a single stage, straight from Java source to dex bytecode. The idea was that
jack would do the entire build, in one toolchain, and without unnecessary conversions – making it much faster.
Unfortunately, that approach hit some major speedbumps.
First, that original two-stage build process was only part of the story. For many developers, the process was actually three or more stages:
For developers using those additional stages, Jack needed to provide similar functionality. In the case of shrinking, Google actually provided support via their own implementation inside of Jack. But those third-party plugins no longer worked because they didn’t have any bytecode to operate on. This was a big issue for anyone who used those plugins – and there were many plugins and many users.
Second, some of Google’s other build features – including a key one, Instant Run – didn’t work with the Jack toolchain. This was ironic because Instant Run had effectively the same goal as Jack: to help developers work faster.
So Jack wasn’t a clear win for the development community. But even so, Google doubled down on it, announcing that they would only support Java 8 language features for developers who were using the Jack toolchain. This was meant to provide an incentive for developers to migrate to Jack, but the community reaction wasn’t positive. In essence, it backfired.
Why is this history important? First, it shows how dedicated Google is to build performance, and how far they are willing to go to get it. Second, it explains how we came to have the build toolchain of 2017:
At that point, in 2017, the builds were architecturally even more complex than they were in 2014.
Google did not give up. Later that same year (2017), Google introduced D8, an optional replacement for the
dx dex compiler, explicitly designed to run faster and make smaller output binaries. Then they moved the
desugar stage into
d8, thereby simplifying the architecture and making the build process even faster. With
d8, the build process looks like this:
In 2018, Google made D8 the default dex compiler.
Now, in 2019, Google has introduced
r8, which is a replacement for ProGuard and has all the features of
d8, so now the build process looks like this:
This is another big architectural improvement – one less tool in the toolchain, and
r8 is both faster and better at shrinking than ProGuard. R8 is better all around.
Google appears to be headed in the same direction as they were with Jack: to a one-stage build process that goes directly from Java source to Dex. Now that they have the entire toolchain under their control, we believe they’ll feel more-able to continue to evolve the architecture, and the next obvious architectural change is to skip the bytecode stage entirely.
Clearly that’s a huge change for third-party plugins, and Google will have to take is slowly if they want it to be accepted. They have demonstrated, with D8 and R8, that they are willing to do these changes one at a time, so we think they’ll take a more-measured approach to this change – and probably succeed.
In conclusion, we don’t see R8 and “just another tool” – it’s “another important step” in an architectural vision that may result in a pure-dex build process, eventually.
With that vision in mind, you might imagine that we have big changes coming for DashO (our powerful Java obfuscation and application protection tool that integrates tightly with Android) – and we do! We don’t like to talk “futures,” but I will say that our customers who are testing early versions of our new approach are excited about the changes.
If you’d like to learn more, please don’t hesitate to contact us – we love to talk with our customers and teams who want better protection for their apps!