Exploring the Android build process: Caching
Recently we decided to investigate the slow build speeds of the Android application @ASOS. This post is part of a short series about how we approached the problem, what we tried and what we found out. To be clear, don’t expect miracles and 🦄 here, but you’ll get a better understanding of what you can do to optimise your builds.
Enabling caching is a low-hanging fruit — it’s easy to do and immediately improves build speeds. I’ll summarise the different types of caching available today (Gradle 4.7, Android Gradle Plugin 3.1.1, Kotlin 1.2.40). These are just part of the settings you can tweak to improve your build speed — we’ll take a look at other options in a future post.
TL;DR — there are three different caches that affect your builds — Android cache, Gradle cache, Kapt cache.
Introduced as an experimental feature in the Android Gradle Plugin 2.2 and controlled by this flag in your gradle.properties file:
It’s turned on by default since Gradle Plugin 2.3.0, so if you’re using a later version (as you should!) just make sure you haven’t switched it off. The location of the cache is one of the following paths, based on the environment variables set on your machine:
$ANDROID_SDK_HOME/.android/build-cache/ # 1st priority
$USER_HOME/.android/build-cache/ # 2nd priority
$HOME/.android/build-cache/ # 3rd priority
A single cache directory holds all stored artefacts, so you can re-use libraries used by different projects/branches/builds. You can read more details about what exactly gets cached HERE.
The Android cache is cleaned by the command:
Introduced a while ago and production-ready for Java / Android projects since Gradle 4.0. It’s controlled by this flag in your gradle.properties file:
By default artefacts are stored in:
This cache stores the full output of a cacheable task. For best results make sure your project uses at least Android Gradle Plugin 3.1 and Kotlin version 1.2.21 or up (that’s the first one that makes many Kotlin tasks cacheable).
The only way I found to clear this cache is to manually remove the cache folder:
rm -rf ~/.gradle/caches/build-cache-<x>
Read the full documentation about it HERE. If your build is set-up correctly and you’re happy with the cache results, be sure to check THIS great guide that explains how to take your caching to the next level — sharing cache results between your whole team and even your CI.
Just imagine — you checkout a branch that’s already built by the CI and your local build is almost instant — awesome, right?
This one applies only to projects that use Kotlin and have some annotation processing involved. Including it here, as many popular libraries like Dagger, ButterKnife, or the Android Architecture Components require the use of Kotlin’s annotation processor, or kapt for short.
The feature is still experimental (at the time of writing) and must be applied with caution. In short, it works correctly with annotation processors that have stable outputs (same input generates the same output) and that don’t modify files outside of the Java and Kotlin classpaths. Please read the comments here to find out more about these limitations.
To enable the kapt cache add this in the build.gradle file of a module that uses kapt:
Cleaning the cache
Every once in a while you might want to really clean your project, including all related caching — for example, to measure how long a clean build takes, or you’ve experienced a bug with any of the caches (rare, but happens). To do so, you need to run:
./gradlew cleanBuildCache # clean Android cache
rm -rf ~/.gradle/caches/build-cache-<x> # clean Gradle cache
./gradlew clean # clean your project
Last thing, don’t mistake any of the above with the menu File -> Invalidate Caches / Restart … in Android Studio, which allows you to clear your IDE’s caches, indexes, etc. Read more about what is stored (and cleaned) HERE.
I hope that’s a useful start in your build optimisation journey. Stay tuned for part 2!