Image for post
Image for post

Shrinking Your App with R8

Android Developers
Jul 28 · 5 min read

Posted by Søren Gjesse, Software Engineer and Christoffer Adamsen‎, Software Engineer

Apps with a smaller download size and install footprint are far more likely to be installed and stay installed, particularly in emerging markets. With the R8 compiler, you now have more comprehensive support to achieve that through shrinking, obfuscation, and optimization.

In this post, we provide an overview of the features available in R8, the reduction in code size you might expect, and show how to enable these features in R8.

R8 shrinking features

R8 offers four features designed to reduce the size of your Android app:

  • Tree shaking: Using static code analysis to find and remove unreachable code and uninstantiated types.
  • Optimization: Optimizing code for size by removing dead code, selective inlining, unused argument removal, and class merging.
  • Identifier renaming a.k.a. obfuscation: Using short names and squashing package namespace.
  • Reducing debug information: Canonicalizing debug information and compressing line number information.

Why we need R8 shrinking

When you write an app, all the code should serve a purpose and implement features in the app. However, most apps use third-party libraries, such as Jetpack, OkHttp, Guava, Gson and Google Play Services, and apps written in Kotlin always include the Kotlin standard library. When you use one of these third-party libraries, often only a very small part of each individual library is used in your app. Without shrinking, all the library code is retained in your app.

Your code may also be larger than it needs to be because verbose code sometimes improves readability and maintainability: for example, you may strive to use meaningful variable names and the builder pattern to make it easier for others to review and understand your code. But these patterns come at the expense of code size — very often, the code you write yourself offers plenty of opportunity for shrinking.

Enable R8 shrinking of your app

To have R8 shrink your app for release builds set minifyEnable to true in your app’s main build.gradle file, like this:

Don’t let the minifyEnable name confuse you — it turns on R8 shrinking.

How much will R8 reduce your app’s size?

R8 can substantially reduce your app’s size. For example, last year’s Google I/O app was 18.55 MB with 150,220 methods and 3 dex files before shrinking. After shrinking, the app reduced to 6.45 MB with 45,831 methods and 1 dex file. R8 saved 65% in dex size (measured with Android Studio 3.5.1 and the IOSched sample app at this commit).

The basic shrinking algorithm

For simplicity, let’s take an example of a standalone program in the Java programming language:

The entry point for the program is the static void main method, which we specify using the following keep rule:

The R8 shrinking algorithm works as follows:

  • First, it traces all reachable code from the well-known entry points of the program — these entry points are defined by R8 keep rules. For example, in this Java code example, R8 would start at the main method.
  • In the example, R8 traces from the main method to the greeting method. The greeting method calls into the runtime, so tracing stops there.
  • With tracing complete, R8 uses an optimization called tree shaking to remove unused code. In the example, tree shaking removes the method unused since R8’s tracing step detects that that method is not reachable from any well-known entry points.
  • Next, R8 renames identifiers to shorter names that take up less space in DEX files. In the example, R8 might rename the method greeting to the shorter name a:
  • Finally code optimizations are then applied. One of these is inlining when that leads to less code. In this example moving the body of the method a into main directly will make the code smaller:

As you can see, the resulting code is much smaller than the original.

Preparing an app for R8 shrinking

Like a standalone Java program, an Android application has a number of well-known entry points: activities, services, content providers, and broadcast receivers. The aapt2 tool handles these entry points for you by generating keep rules based on your Android Manifest file.

In addition to these well-known entry points, there are other standard keep rules needed for Android applications. These rules are provided by the Android Gradle Plugin in a default configuration file which you specify when configuring your build:

Reflection in app code

Reflection results in code entry points that R8 does not recognize when tracing your code. Remember that reflection can also happen in third-party libraries and as third-party libraries are effectively part of your app, you — as the app developer — effectively become responsible for the reflection performed in these libraries as well as in your own code. Libraries might come with their own rules included, but remember that many libraries are not written with Android or with shrinking in mind, so they might need additional configuration.

Take this example of a Kotlin class with a field called name and a main method that creates an instance and serializes that instance to JSON:

After shrinking the code, running the program outputs an empty JSON object {}. This is because R8 only sees the field name as written (which happens in the Person constructor) but never read, so R8 removes it. Person ends up with no fields causing the empty JSON object. However, the field is read by the Gson serializer, but Gson uses reflection techniques to do so, so R8 does not see that this field is read.

To keep the name field, add a keep rule in your proguard-rules.pro file:

This rule instructs R8 not to touch the field called name in the class Person. With this in place, running the code gives the expected JSON object of {“name”:”Søren Gjesse”}.

Finally, when setting up your project, be sure to add the proguard-rules.pro file to your build.gradle configuration:

Learn more

Interested in learning more about how the R8 shrinker works? Check out the R8 developer documentation and watch this talk from Android Dev Summit 2019 to learn about R8 in general and a deep dive into one of R8’s more advanced optimizations — class inlining:

You can click here to go straight to the section on class inlining.

Android Developers

The official Android Developers publication on Medium

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store