Exploring the Android build process: demystifying Gradle flags

This is Part 2 of a short series about optimising your build speeds. If you haven’t already, please check out Part 1, which describes the different build caches you can use.

In this post, I’ll explain some other build properties you can tweak. Let’s start with the Gradle ones.

Gradle properties

The following list is just a subset of all available properties — these are the ones I found myself tweaking the most. All of these can be set in the gradle.properties file of your project.

TL;DR — get the best results from Gradle by using:

org.gradle.caching = true
org.gradle.parallel = true
org.gradle.configureondemand = true*
org.gradle.daemon = true
org.gradle.jvmargs=-Xmx<some_value**>m

org.gradle.caching=true — enable the Gradle build cache. For more info about this, please read Part 1 of this blog series.

org.gradle.parallel=true — enable Parallel project execution. This allows the execution of tasks from different projects in parallel. All modules in your Android app count as separate ‘projects’ in Gradle terms, so this feature is especially useful for modularised apps. Run this command to see the different ‘projects’ Gradle sees in your app:

./gradlew projects

org.gradle.configureondemand=true — enable Configure-on-Demand. This feature can save you a couple of seconds from the Configuration phase of Gradle, as only ‘projects’ (modules in Android terms) related to the current tasks are configured. Only helpful if your app has a large number of modules.

* At the time of writing there is an incompatibility issue between Gradle 4.6/4.7, Android Plugin for Gradle 3.1.1/3.1.2 and this flag. When using the latter versions of both you must switch Configure-on-Demand off, otherwise your project won’t build at all. Switching it off is a two step process — first turn off the flag in gradle.properties and second — uncheck the Configure-on-Demand property in Android Studio -> Preferences -> Build, Execution, Deployment > Compiler.

org.gradle.daemon=true — enable the Gradle Daemon. This is a long-lived Gradle process on your machine. The big improvements come from reusing computations from previous builds. This is enabled by default since Gradle 3.0, so just make sure you haven’t switched it off.

org.gradle.jvmargs=-Xmx<some_value>m — set the memory allocated for the Gradle daemon.

** It’s not a ‘one-size-fits-all’ situation and more memory is not always better here — just experiment with a few values until you find the best case. Keep this at least 2GB though, as that’s the minimum required to enable the ‘dex-in-process’ optimisation.

‘Dex-in-process’ was introduced a while ago with Android Studio 2.1. It allows dex to run within the Gradle daemon process, thus avoiding the costly spinning of new processes for dex. The gain here is quite significant, but this optimisation will not take place if there’s not enough memory in the Gradle daemon process for it (thus the 2GB minimum). Read more about it HERE.

DexOptions

The Android Plugin for Gradle supports a few extra settings in it’s DSL. We’re mostly interested in the dexOptions section.

TL;DR — with previous versions of Gradle and Android Plugin for Gradle tweaking the dexOptions made sense. When using the latest ones though, in most cases you’ll be better off removing this configuration block completely.

If you still decide to tweak the values — add a dexOptions section in your app’s module build.gradle file:

apply plugin: 'com.android.application'

android {
  ...
  
  dexOptions {
    <properties go here ...>
  }
  
}

maxProcessCount — set the number processes to use for dexing if dex-in-process isn’t enabled for the build. This should never happen and you shouldn’t need to set this flag. If you do, tweak org.gradle.jvmargs as discussed above to give enough memory for dex-in-process.

javaMaxHeapSize — set the Java max heap size for the dex process. This used to have positive effects on build speeds before dex-in-process was introduced (combined with the default value of 4 for maxProcessCount), but nowadays it’s best not to use this setting. Still, if you do tweak it — ensure you leave enough memory to allow dex-in-process:

org.gradle.jvmargs = javaMaxHeapSize + extra # extra >= 1GB

jumboMode — it relates to the number of strings that can be referenced in a DEX file, which is 2¹⁶. Contrary to a few StackOverflow answers, it’s not directly related to maximum allowed app size or number of methods in a DEX file. Enabling jumboMode allows 2³² strings to be referenced. Off by default, but you’ll very rarely need to set this one.

preDexLibraries — pre-dexes all dependencies during the first compilation of the project. This makes clean builds a bit slower, but incremental ones faster as these pre-dexed resources are reused. Works very nice with the Android build cache and it’s turned on by default in Android Plugin for Gradle 3.1.2.

incremental — this option is now deprecated and will be completely removed in the end of 2018! With recent versions of the Android Gradle Plugin dexing is incremental whenever possible (when not using legacy multiDex and always if targeting minSdkVersion 21+).

Hope you find these properties useful. Stay tuned for Part 3, where I’ll discuss a very hot topic in the Android community — whether modularising an app improves build speeds.

Submit a Comment

Your email address will not be published. Required fields are marked *