Optimize Gradle build performance

Prashant Panwar
6 min readSep 1, 2019

--

Source: https://dribbble.com/shots/3251701-Rocket-elephant

Gradle is the dominant multi-platform build system. It solves one of the hardest problem faced by android developers, how to automate the building and testing apps to achieve rapid productivity and how to manage dependencies and variations that allows professional developer pump out dozens of variances of their app with one click. It provides a flexible way to compile, build, and package your Android app or library.

Think of Gradle as the one-stop-shop for defining and integrating all parts of your software delivery process.

Unfortunately, the default configurations aren't enough and need a little bit of performance boost. Adding a new library or updating an existing one is a time-consuming process, and you have to wait for Gradle to finish up the sync, which is really annoying sometimes.

So, it’s important that you should optimize your Gradle build timing because the less time you spend watching Gradle build your project, the more time will spend writing awesome apps.

This article offers some techniques to help you resolve build speed bottlenecks and optimize build speed. The general process of improving your build speed is as follows:

  1. Profile your build to identify and diagnose some of the trickier bottlenecks that may be specific to your project or workstation.
  2. Optimize your Gradle properties by applying modifications to decrease build performance.
  3. Optimize your build configuration by taking a few steps that immediately benefit most Android Studio projects.

Let’s dive further and see if we can decrease the build performance. It is advisable to use dry-run command while profiling after each tip/trick, as it tells Gradle to evaluate the project but don’t run any task. You can use the following command:

./gradlew --profile --dry-run assembleDebug

Profile your build:

Projects with a large codebase and custom logic require a deeper look into the build process to find bottlenecks. Profiling tells how long Gradle takes to execute each phase of the build lifecycle and each build task.

Using profiling to improve your build speed typically involves running your build with profiling enabled, making some tweaks to your build configuration, and profiling some more to observe the results of your changes. You can find that report under /projectDir/build/reports/profile directory. To generate and view the build report:

  1. Open terminal (select View > Tool Windows > Terminal)
  2. Perform a clean build by entering the following command:
./gradlew clean

3. Execute a debug build of one of your product flavors, such as the “prod” flavor, with the following command:

./gradlew --profile --offline --dry-run assembleFlavorDebug

Optimize your Gradle properties:

Gradle provides several options that make it easy to configure its behavior and process that will be used to execute your build. Setting up a consistent environment for your build is as simple as placing these settings into a “gradle.properties” file. The configuration can be applied as the following:

  • org.gradle.configureondemand=true
    Enables incubating configuration on demand, where Gradle will attempt to configure only necessary projects.
  • org.gradle.daemon=true
    Daemon keeps the instance of the Gradle up and running in the background even after your build finishes
  • org.gradle.parallel=true
    If you have multiple modules in your project, then by enabling this, Gradle can run build operations for independent modules parallelly.
  • org.gradle.caching=true
    Gradle will reuse task outputs from any previous build, when possible, resulting in much faster builds
  • org.gradle.jvmargs=“your JVM arguments”
    Specifies the JVM arguments used for the Gradle Daemon. The setting is particularly useful for configuring JVM memory settings for build performance. Arguments may vary from machine to machine, use JVM args as follow:
org.gradle.jvmargs=-Xmx3072m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
  • android.databinding.incremental=true
    Data binding annotation processor supports incremental annotation processing and results in improved incremental build performance
  • kapt.incremental.apt=true
    KAPT 1.3.30 supports incremental annotation processing

After applying these configurations profile your project again and check the results.

Optimize your build configuration:

Follow these tips to improve the build speed of your Android Studio project. Especially useful when you’re working on a large project. You can peek and choose based on your project setup.

  • Keep tools up-to-date:
    The Android tools receive build optimizations and new features with almost every update. To take advantage of the latest optimizations, keep the following up to date:
    Android Studio
    SDK tools
    → The Android plugin for Gradle
  • Create Build Variants:
    Many of the configurations you need for a “release” version are not required when you’re using a “debug” version. Enabling unnecessary build processes slows down your incremental and clean builds. For example, creates a “dev” flavor and a “prod” flavor:
android {
...
defaultConfig {...}
buildTypes {...}
productFlavors {
// When building a variant that uses this flavor, the following configurations override those in the defaultConfig block.
dev {
versionNameSuffix "-dev"
applicationIdSuffix '.dev'
}

prod {
// defaultConfig block for the release version of your app
}
}
}
  • Avoid Compiling unnecessary resource:
    Avoid compiling and packaging resources that you aren’t testing (such as language, localizations, and screen-density resources). You can do this by specifying only the resource you need as follow:
android {
...
productFlavors {
dev {
...
resConfigs "en", "xxhdpi"
}
...
}
}
  • Use static dependency versions:
    You should avoid using version numbers with a plus sign at the end. Such as “com.android.tools.build:gradle:3.+”. This may result in unexpected version update, difficult to maintain dependencies and slower build cause by Gradle checking for updates.
  • Enable Offline Mode:
    If you’re on a slow network connection, build performance may suffer as Gradle try to use the network to resolve dependencies. You can avoid this by enabling offline mode as:
    1. Open File > Settings
    2. Click Build, Execution, Deployment > Gradle
    3. Check Offline Work checkbox and press Appy and Ok
  • Create library Modules:
    Look for code in your application that you can convert into an Android library module. Modularizing your code this way allows the build system to compile only the modules you modify and cache those outputs for future builds. It also makes parallel project execution more effective (when you enable that optimization).
  • Disable Crashlytics for debug build:
    If you don’t need to run a Crashlytics report, speed up your debug builds by disabling the plugin as follows:
android {
...
buildTypes {
debug {
ext.enableCrashlytics = false
}
}

If you want to use Crashlytics with your debug builds, you can still speed up incremental builds by preventing Crashlytics from updating app resources with its own unique build ID during every build.

android {
...
buildTypes {
debug {
ext.alwaysUpdateBuildId = false
}
}
  • Enable single-variant project sync:
    If your project uses multiple build variants, you can now optimize project syncs by limiting them to only the variant you have currently selected.
    You need to use Android Studio 3.3 or higher with Android Gradle Plugin 3.3.0 or higher to enable this optimization. To enable this optimization, click File > Settings > Experimental > Gradle (Android Studio > Preferences > Experimental > Gradle on a Mac) and select the Only sync the active variant checkbox.
  • Enable the build cache:
    Build cache stores certain outputs that the Android plugin for Gradle generates when building your project (such as unpackaged AARs and pre-dexed remote dependencies). Your clean builds are much faster while using the cache.
  • Convert images to WebP:
    WebP is an image file format that provides lossy compression (like JPEG) as well as transparency (like PNG) but can provide better compression than either JPEG or PNG. Reducing image file sizes, without having to perform build-time compression, can speed up your builds.
  • Disable PNG crunching:
    You can speed up your build by disabling automatic image compression every time you build your app. If you’re using Android plugin 3.0.0 or higher, PNG crunching is disabled by default for only the “debug” build type. To disable this optimization for other build types, add the following to your “build.gradle file:
android {
...
buildTypes {
release {
// Disables PNG crunching for the release build type.
crunchPngs false
}
}
  • User Incremental Annotation Processor:
    To improve incremental build speeds, you should update your Android Gradle plugin and use only incremental annotation processors whenever possible.

After applying these optimizations, profile your project again and see the final results. These optimizations don’t just help you in faster build but also in the overall maintainability of your project.

Thank you for reading and Happy Coding!

--

--