Faster Android Build Times

As this year’s Google I/O (2017) approached, the build times of the project I was working on spiked up exponentially. Clean builds that took 3–4 mins were taking 13–14 mins and incremental builds which took less than a min were taking close to 6 mins. So why the sudden exponential increase in build times?

Because we forked out into a multi-module structure for our application. A lot of Android applications out there are just a monolith of a single module of type com.android.application . We forked over to a project structure where features were represented by modules which were basic libraries ( com.android.library ), which were then imported over into a dependency tree which eventually made it into an APK. From one module containing all the code, we moved over to 18 modules containing feature specific code to enforce code isolation. Subsequently, our build times rocketed up.

The reason for this was that the gradle plugin (which at the time was on 2.3.3 I believe) didn’t do a good job with multi-module projects. Android Studio usually does a good job about deciding whether it needs to compile code again depending on what code is changed etc. However, it didn’t with multi-module projects.

At Google I/O they announced the all new Android Studio 3.0 along with Gradle plugin 3.0. Currently, Android Studio and the Gradle plugin are at beta 4 and besides some small issues, I find that the newer Android Studio is stable enough (at least for the project I am working on).

At the Speeding up your Android Gradle Builds talk, they gave a few more tips (besides upgrading to Android Studio 3.0). Here they are :

Disable Splits

A lot of you might be using split APKs for your PlayStore release builds. Splits are a great way of cutting down on APK size so that lower res APKs don’t have to bundle resources for higher res devices (this also works for architectures where x86 architecture won’t have to bundle other architecture code). However, the splitting itself takes time and isn’t really required for debug builds. You can disable splits by adding this in your build.gradle file.

android {
...
if (project.hasProperty('devBuild') {
splits.density.enable = false
}
}

What is this devBuild? It’s a flag that we can pass to the compiler which will make sure that splits are disabled when passing that flag.

You can add that flag in Android Studio. Go to Android Studio -> Preferences -> Compiler and add it as such

Enable Configure on Demand

As shown in the above screenshot, you can enable Configure on Demand option. This may or may not work for you since it is an incubating feature. If you find that build times creep up, you might want to disable it.

You can also enable it by adding this to your gradle.properties file

org.gradle.configureondemand = true

Enable Parallel Builds

In the same screen, you can also enable parallel execution for multi-module builds. This might buy you faster build times if you have multiple modules and they are independent. You can enable it in your gradle.properties file :

org.gradle.parallel = true

Again, this is an incubating feature and Gradle itself asks us to throw caution when trying this.

Disable PNG Crunching for Debug Builds

PNG crunching is a great tool to reduce your APK size. However, it takes significant computations and adds to your build time. While this is fine for a release build, you don’t really want to crunch your PNGs for debug builds. You can disable it by adding this to your build.gradle file.

android {
...
if (project.hasProperty('devBuild') {
splits.density.enable = false
aaptOptions.crunchedEnabled = false
}
}

Because we already added the devBuild option to Android Studio compiler settings, this should also start working.

Enable Gradle Caching

Enabling Gradle caching will help in compile avoidance whereby Gradle will cache the outputs of your code and will try and reuse them when possible. You can enable it by adding org.gradle.caching = true to your gradle.properties file.

Careful While Using Crashlytics

Crashlytics is an indispensable tool for most Android developers. However, Crashlytics apparently does a weird thing where it generates a new build number every millisecond. Crashlytics’ build number is tied to the timestamp. And this is very important for release builds so that every build is unique. However, it’s not really required for debug builds. So you can disable it like so :

android {
...
debug {
ext.alwaysUpdateBuildId = false
}
....
}

Upgrade to Gradle Plugin 3.0

Last but not the least, a lot has been done in Gradle 3.0 which will reduce build times. There are new APIs to import libraries ( compile is deprecated and we now have implementation and api )

For a brief overview : in a multi-module build structure, implementation will mean that the dependency is only propagated to whoever is importing it and not to the consumers of the importer. api on the other hand will propagate the dependency all the way to the top of the dependency tree. More on that here.

Upgrading to Android Studio 3.0 Beta 4 itself reduced our build times significantly. We still use Gradle plugin 2.3.3 since our migration to 3.0 is not over yet. Our clean build times are down to 2.5–3 mins from 13–14 mins and incremental builds are usually below 30 seconds from 6 mins. That is a huge deal and I can’t wait to test the build times when we actually move to the 3.0 Gradle plugin.

And Now.. A Word of Caution

Build times are extremely variable. That is something that everyone has to realize. A lot of times, you are responsible for your own build times. Everything from your computer’s RAM, the Java Heap space your provide, your project structure, your code, what code you change, how many resources you have etc. adds variability to build times. Hence, the tips I gave work well for me and are generally known to work. However, they may have different effects for you. Your build times may not reduce drastically if they were not drastically high to begin with. And that’s why…

Profile Your Builds

Slow builds are not acceptable. That needs to be said. A developer is a lot more productive when builds are faster and they can iterate faster. If your builds are slow, find out the pain points and correct them.

Run your gradle build with option : dry-run — info — profile

Benchmark your builds with : — benchmark . Builds are variable and I would recommend at least 3 builds to see what your build times are.

I would highly recommend watching the talk at Google IO. I am leaving a link here.

Till then, happy coding and may faster build times be with you 😄