Nobody likes to wait endlessly for their projects to build. Here are some methods that we at Tooploox employ to spend more time creating great apps, and less time building them.
Use the newest Gradle distribution
Gradle keeps getting faster and faster with each release, and it rarely makes sense to use older version. Android Studio keeps generating gradle wrapper that is ages behind — Android Studio 2.3 Canary 2 still uses version 2.14.1 — already few months old as of writing this article. Unless it breaks your build — and it shouldn’t — update.
To update the gradle wrapper for your project, simply run
./gradlew wrapper --gradle-version <<version>> --distribution-type [all|bin]
./gradlew wrapper --gradle-version 3.3 --distribution-type all
This will download specified Gradle distribution into wrapper files. Also, consider periodically removing old versions leftovers from
.gradle folder inside your project.
You can update gradle wrapper from Android Studio as well — open
ProjectStructuredialog and update
Gradle versionfield in
wrappertask is preferred, though, since it also updates the wrapper
jarfile as well as
Despite gradle wrapper being the preferred method of running gradle builds, for non-production builds consider using locally installed gradle distribution. This way you can benefit from the latest performance improvements while not having to modify (possibly someone else’s) project’s gradle wrapper. To install gradle locally, refer to official website, or use packet manager like Macports or Homebrew. And update it periodically!
Configure Gradle properly
Gradle is extremely complex build system and as such it can be configured extensively. We’ll cover only some options — the ones that are the most important from the performance standpoint.
Project-wide settings for your gradle build are kept in
gradle.properties file in your project's root folder.
System wide settings can be found in
.gradle folder in your HOME directory -
~/.gradle/gradle.properties. On Windows look in
First Gradle reads all project-wide options, followed by system-wide options. If an option is specified twice, the last one wins. This essentially means you always override project’s properties by your system-wide options.
org.gradle.daemon- should be set to
trueto take advantage of Gradle daemon. Long story short, daemon means reusing things in subsequent builds
org.gradle.jvmargs- specifies jvmargs used for daemon process. You want to set high enough memory limit to fix dex in Gradle process, so minimum configuration here is
-Xmx2g, while 3-4 gigabytes won't hurt either
org.gradle.configureondemand- slight optimisation that, if set to
true, configures only needed modules of the project. Useful in large, multi-module projects
org.gradle.parallel- allows Gradle to build modules in parallel. Only useful, if your modules don't depend on each other
gradle.properties file for a 16GB machine looks like this:
-Xmxargument should be set to at least your
dexOptions.javaMaxHeapSizeoption plus some memory for Gradle build itself. If you don't specify dex heap size manually, 1GB is used by default.
Android has partial (no transparency and no lossless encoding) support for
webp format since Android 4.0 (API 14), and full support came with API 18. If you're lucky enough to be able to use it, Android Studio 2.3 offers quick fix that converts your existing
webps - simply right-click image (or a folder, even
res) and choose
Convert to WebP.... This can shave off several megabytes, from your APK, which may also speed up sending your application to the device. One thing to keep in mind is that
webp images aren't supported as launcher icons!
Edited: processing or merging resources is not, in fact, related to dexing. Previously stated significant speedup from using
pngwas the result of
aaptnot crunching images anymore. Thanks to Jake Wharton for pointing this out!
Do not crunch your
If you’re not lucky enough to use
webps, you can still speed up your build by disabling
png crunching. From Android documentation on drawables:
Image resources placed in res/drawable/ may be automatically optimized with lossless image compression by the aapt tool during the build process. For example, a true-color PNG that does not require more than 256 colors may be converted to an 8-bit PNG with a color palette.
This, obviously, takes time. You can skip this optimisation by providing proper option to
In one of ours
png-heavy projects, this alone reduced build times from 4 minutes to 40 seconds. You may want to reenable this for production build (or disable only for development) though, as crunching is, after all, an optimisation.
Use dex in process
Fortunately this is now enabled by default, but to reiterate — make sure your gradle daemon has enough memory allocated (at least 1GB)!
If at some point your build suddenly slows down, it might be the sign that your heap size is not big enough to fit dex in process anymore. You can then increase dex memory size in your module’s
Remember that afterwards you need to increase heap size for Gradle as well!
Increasing java max heap size for dex process should be used sparingly, as it’s rarely needed.
true may speed up incremental builds.
It can slow down clean builds though, so it’s worth disabling this option for builds in your CI environment.
Set minSdkVersion to 21+ when using Multidex
If you use multidex, you can greatly improve your build times if your
minSdkVersion is set to 21+. You can either create a special
dev flavor for you app that overrides
minSdkVersion, or have the version computed dynamically based on a gradle property:
And then compile with, for example,
./gradlew installDebug -PminSdk=23. You can set
-PminSdk=23 option in
Build->Compiler->Command line options in your Android Studio as well, to use this option for builds triggered from your IDE.
Remember that Android Studio uses specified command line options also for generating signed APK. If you’re preparing a release APK, you might want to use command line, to be sure you haven’t built yours with too high minimum SDK version.
Avoid computations in Gradle configuration phase
When you trigger a build, at first your gradle projects are configured. This (more or less) runs the parts of your gradle build scripts that aren’t tasks. There’s a popular trick of having git sha in your
The problem here is that every time you run a task,
getGitHash will be run. While this particular action may not be very inefficient in itself, lots of such small actions pile up. Consider only running these when building certain flavours, like staging builds, or only on CI environment.
This is also important when applying external plugins. You most likely use Crashlytics in your app — for development purposes it is a good idea to disable not only crashes reporting (directly in your application), but also Fabric plugin itself:
Limit packaged resources configurations
In projects with multiple configurations (like translations) you can save some time by only packaging resources for single language or screen size:
You can mix configurations, so
resConfigs "en", "xhdpi" would be also valid, as well as for example
resConfigs "en", "fr", "xxhdpi".
Long story short, there is now no reason to use
jcenter() in your
JCenter is bigger, more secure and - even if ever so slightly - faster.
Use specific dependencies versions
Aside from being bad practice, using
+ in dependencies versions forces gradle to check for newest dependency version each time you build your project. Always use specific library version, like
compile 'com.squareup.retrofit2:retrofit:1.9.0' instead of
Use instant run (sometimes)
Instant run speeds up builds immensely — if it works. Keep an eye on weird bugs or inexplicable errors. In bigger projects (or for some other reason less-instant-run-friendly projects) you might be better off spending more time building, and less time debugging non-existent errors.
Simply put, Gradle is written in Java, so it’ll benefit from performance improvements in newer JVM implementations. While Android Studio now bundles its own JDK, if you still use older Java on your machine, updating it will also speed up Gradle outside Android Studio.
Above all, you should always profile and see where there’s place for improvement. Doing this for Gradle build is as simple as providing
--profile option in the command line:
gradle app:assembleDebug --profile
build/reports/profile directory, you'll find an HTML report with summary of times and details for both configuration phase and task execution. This will give you some insight into which parts of your build deserve attention.
That’s it. Let me know what helped you most, and what other tricks you use to speed up your builds!
This article originally appeared on Tooploox company blog.