Long build times slow down your development process. This page provides some techniques to help resolve build speed bottlenecks.

The general process of improving your app's build speed is as follows:

1. [Optimize your build configuration](https://developer.android.com/build/optimize-your-build#optimize)by taking a few steps that immediately benefit most Android Studio projects.
2. [Profile your build](https://developer.android.com/studio/build/profile-your-build)to identify and diagnose some of the trickier bottlenecks that may be specific to your project or workstation.

When developing your app, deploy to a device running Android 7.0 (API level 24) or higher whenever possible. Newer versions of the Android platform implement better mechanics for pushing updates to your app, such as the[Android Runtime (ART)](https://source.android.com/devices/tech/dalvik/)and native support for[multiple DEX files](https://developer.android.com/studio/build/multidex).

**Note:**After your first clean build, you may notice that subsequent builds, both clean and incremental, perform much faster even without using any of the optimizations described on this page. This is because the Gradle daemon has a "warm-up" period of increasing performance---similar to other JVM processes.

## Optimize your build configuration

Follow these tips to improve the build speed of your Android Studio project.

### Keep your tools up to date

The Android tools receive build optimizations and new features with almost every update. Some tips on this page assume you're using the latest version. To take advantage of the latest optimizations, keep the following up to date:

- [Android Studio and SDK tools](https://developer.android.com/studio/intro/update)
- [The Android Gradle plugin](https://developer.android.com/studio/releases/gradle-plugin)

### Use KSP instead of kapt

The Kotlin Annotation Processing Tool (kapt) is significantly slower than the Kotlin Symbol Processor (KSP). If you are writing annotated Kotlin source and using tooling that processes annotations (such as[Room](https://developer.android.com/training/data-storage/room)) that supports KSP, you'll want to[migrate to KSP](https://developer.android.com/build/migrate-to-ksp).

### Avoid compiling unnecessary resources

Avoid compiling and packaging resources that you aren't testing, such as additional language localizations and screen-density resources. Instead, only specify one language resource and screen density for your "dev" flavor, as shown in the following sample:  

### Groovy

```groovy
android {
    ...
    productFlavors {
        dev {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations "en", "xxhdpi"
        }
        ...
    }
}
```

### Kotlin

```kotlin
android {
    ...
    productFlavors {
        create("dev") {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations("en", "xxhdpi")
        }
        ...
    }
}
```

### Experiment with putting the Gradle Plugin Portal last

In Android, all plugins are found in the`google()`and`mavenCentral()`repositories. However, your build might need third-party plugins that are resolved using the[`gradlePluginPortal()`](https://plugins.gradle.org/)service.

Gradle searches repositories in the order that they're declared, so build performance is improved if the repositories listed first contain most of the plugins. Therefore, experiment with the`gradlePluginPortal()`entry by putting it last in the repository block in your`settings.gradle`file. In most cases, this minimizes the number of redundant plugin searches and improves your build speed.

For more information about how Gradle navigates multiple repositories, see[Declaring multiple repositories](https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:declaring_multiple_repositories)in the Gradle documentation.

### Use static build config values with your debug build

Always use static values for properties that go in the manifest file or resource files for your debug build type.

Using dynamic version codes, version names, resources, or any other build logic that changes the manifest file requires a full app build every time you want to run a change, even if the actual change might otherwise require only a hot swap. If your build configuration requires such dynamic properties, then isolate those properties to your release build variants and keep the values static for your debug builds, as shown in the following sample:  

```kotlin
  ...
  // Use a filter to apply onVariants() to a subset of the variants.
  onVariants(selector().withBuildType("release")) { variant ->
      // Because an app module can have multiple outputs when using multi-APK, versionCode
      // is only available on the variant output.
      // Gather the output when we are in single mode and there is no multi-APK.
      val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE }

      // Create the version code generating task.
      val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) {
          it.outputFile.set(project.layout.buildDirectory.file("versionCode${variant.name}.txt"))
      }

      // Wire the version code from the task output.
      // map will create a lazy Provider that:
      // 1. Runs just before the consumer(s), ensuring that the producer (VersionCodeTask) has run
      //    and therefore the file is created.
      // 2. Contains task dependency information so that the consumer(s) run after the producer.
      mainOutput.versionCode.set(versionCodeTask.flatMap { it.outputFile.map { it.asFile.readText().toInt() } })
  }
  ...

  abstract class VersionCodeTask : DefaultTask() {

    @get:OutputFile
    abstract val outputFile: RegularFileProperty

    @TaskAction
    fun action() {
        outputFile.get().asFile.writeText("1.1.1")
    }
  }
```

See the[setVersionsFromTask recipe](https://github.com/android/gradle-recipes/blob/agp-7.3/BuildSrc/setVersionsFromTask)on GitHub to learn how to set a dynamic version code in your project.

### Use static dependency versions

When you declare dependencies in your`build.gradle`files, avoid using dynamic version numbers (those with a plus sign at the end, such as`'com.android.tools.build:gradle:2.+'`). Using dynamic version numbers can cause unexpected version updates, difficulty resolving version differences, and slower builds caused by Gradle checking for updates. Use static version numbers instead.

### Create library modules

Look for code in your app that you can convert into an[Android library module](https://developer.android.com/studio/projects/android-library). Modularizing your code this way allows the build system to compile only the modules you modify and cache those outputs for future builds. Modularization also makes[parallel project execution](https://docs.gradle.org/current/userguide/performance.html#parallel_execution)more effective when you enable that optimization.

### Create tasks for custom build logic

After you[create a build profile](https://developer.android.com/studio/build/profile-your-build), if the build profile shows that a relatively long portion of the build time is spent in the \*\*Configuring Projects\*\* phase, review your`build.gradle`scripts and look for code to include in a custom Gradle task. By moving some build logic into a task, you help ensure that the task runs only when required, results can be cached for subsequent builds, and that build logic becomes eligible to run in parallel if you enable[parallel project execution](https://docs.gradle.org/current/userguide/performance.html#parallel_execution). To learn more about taks for custom build logic, read the[official Gradle documentation](https://docs.gradle.org/current/userguide/more_about_tasks.html).

**Tip:** If your build includes a large number of custom tasks, you might want to declutter your`build.gradle`files by[creating custom task classes](https://docs.gradle.org/current/userguide/custom_tasks.html). Add your classes to the<var translate="no">project-root</var>`/buildSrc/src/main/groovy/`directory; Gradle automatically includes those classes in the classpath for all`build.gradle`files in your project.

### Convert images to WebP

[WebP](https://developers.google.com/speed/webp/)is an image file format that provides lossy compression (like JPEG) as well as transparency (like PNG). WebP 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, especially if your app uses a lot of image resources. However, you may notice a small increase in device CPU usage while decompressing WebP images. Use Android Studio to easily[convert your images to WebP](https://developer.android.com/studio/write/convert-webp#convert_images_to_webp).

### Disable PNG crunching

If you don't[convert your PNG images to WebP](https://developer.android.com/studio/write/convert-webp#convert_images_to_webp), you can still speed up your build by disabling automatic image compression every time you build your app.

If you're using[Android Gradle plugin 3.0.0](https://developer.android.com/studio/releases/gradle-plugin#3-0-0)or higher, PNG crunching is disabled by default for the "debug" build type. To disable this optimization for other build types, add the following to your`build.gradle`file:  

### Groovy

```groovy
android {
    buildTypes {
        release {
            // Disables PNG crunching for the "release" build type.
            crunchPngs false
        }
    }
}
```

### Kotlin

```kotlin
android {
    buildTypes {
        getByName("release") {
            // Disables PNG crunching for the "release" build type.
            isCrunchPngs = false
        }
    }
}
```

Because build types or product flavors don't define this property, you need to manually set this property to`true`when building the release version of your app.

### Experiment with the JVM parallel garbage collector

Build performance can be improved by configuring the optimal JVM garbage collector used by Gradle. While JDK 8 is configured to use the parallel garbage collector by default, JDK 9 and higher are configured to use[the G1 garbage collector](https://docs.oracle.com/javase/9/gctuning/garbage-first-garbage-collector.htm#JSGCT-GUID-ED3AB6D3-FD9B-4447-9EDF-983ED2F7A573).

To potentially improve build performance, we recommend[testing your Gradle builds](https://developer.android.com/studio/build/profile-your-build)with the parallel garbage collector. In`gradle.properties`set the following:  

```text
org.gradle.jvmargs=-XX:+UseParallelGC
```

If there are other options already set in this field, add a new option:  

```text
org.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC
```

To measure build speed with different configurations, see[Profile your build](https://developer.android.com/studio/build/profile-your-build#profiling_different_memorycpu_settings).
| **Note:** Be aware of Gradle[issue #19750](https://github.com/gradle/gradle/issues/19750): setting the`org.gradle.jvmargs`property can lead to "Daemon disappeared" failures. Until the bug is fixed you must explicitly set JVM argument defaults. The key JVM arguments that you must explicitly set are the`-XX:MaxMetaspaceSize=256m`and`-XX:+HeapDumpOnOutOfMemoryError`arguments; for the full list of arguments, see the[`java`command documentation](https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html).

### Increase the JVM heap size

If you observe slow builds, and in particular the garbage collection takes more than 15% of the build time in your[Build Analyzer](https://developer.android.com/studio/build/build-analyzer)results, then you should increase the Java Virtual Machine (JVM) heap size. In the`gradle.properties`file, set the limit to 4, 6, or 8 gigabytes as shown in the following example:  

```text
org.gradle.jvmargs=-Xmx6g
```

Then test for build speed improvement. The easiest way to determine the optimal heap size is to increase the limit by a small amount and then test for sufficient build speed improvement.
| **Note:** If you change the default memory limit, you also have to set the`-XX:MaxMetaspaceSize=1g`argument due to[Gradle issue #19750](https://github.com/gradle/gradle/issues/19750).

If you also use the[JVM parallel garbage collector](https://developer.android.com/studio/build/profile-your-build#experiment-gc), then the entire line should look like:  

```text
org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g
```

You can analyze the JVM memory errors by turning the[HeapDumpOnOutOfMemoryError](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/clopts001.html)flag on. By doing so, the JVM will generate a heap dump, when running out of memory.

### Use non-transitive R classes

Use non-transitive[`R`classes](https://developer.android.com/reference/android/R)to have faster builds for apps with multiple modules. Doing so helps prevent resource duplication by ensuring that each module's`R`class only contains references to its own resources without pulling references from its dependencies. This leads to faster builds and the corresponding benefits of compilation avoidance. This is the default behavior in Android Gradle plugin 8.0.0 and higher.

Starting with Android Studio Bumblebee, non-transitive`R`classes are on by default for new projects. For projects created with earlier versions of Android Studio, update them to use non-transitive`R`classes by going to**Refactor \> Migrate to Non-Transitive R Classes**.

To learn more about app resources and the`R`class, see[App resources overview](https://developer.android.com/guide/topics/resources/providing-resources).

### Use non-constant R classes

Use non-constant[`R`class](https://developer.android.com/reference/android/R)fields in apps and tests to improve the incrementality of Java compilation and allow for more precise resource shrinking.`R`class fields are always not constant for libraries, as the resources are numbered when packaging the APK for the app or test that depends on that library. This is the default behavior in Android Gradle Plugin 8.0.0 and higher.

### Disable the Jetifier flag

Since most projects use AndroidX libraries directly, you can remove the[Jetifier](https://developer.android.com/studio/command-line/jetifier)flag for better build performance. To remove the Jetifier flag, set`android.enableJetifier=false`in your`gradle.properties`file.

The Build Analyzer can perform a check to see whether the flag can be safely removed to enable your project to have better build performance and migrate away from the unmaintained Android Support libraries. To learn more about the Build Analyzer, see[Troubleshoot build performance](https://developer.android.com/studio/build/build-analyzer).

### Use the configuration cache

The[configuration cache](https://docs.gradle.org/current/userguide/configuration_cache.html)lets Gradle record information about the build tasks graph and reuse it in subsequent builds, so Gradle doesn't have to reconfigure the whole build again.

To enable the configuration cache, follow these steps:

1. Check that all project plugins are compatible.<br />

   Use the[Build Analyzer](https://developer.android.com/studio/build/build-analyzer#warnings-types)to check whether your project is compatible with the configuration cache. The Build Analyzer runs a sequence of test builds to determine whether the feature can be turned on for the project. See[issue #13490](https://github.com/gradle/gradle/issues/13490)for a list of plugins that are supported.
2. Add the following code to the`gradle.properties`file:

   <br />

   ```text
     org.gradle.configuration-cache=true
     # Use this flag carefully, in case some of the plugins are not fully compatible.
     org.gradle.configuration-cache.problems=warn
   ```

   <br />

When the configuration cache is enabled, the first time you run your project the build output says`Calculating task graph as no configuration cache is available for tasks`. During subsequent runs, the build output says`Reusing configuration cache`.

To learn more about the configuration cache, see the blog post[Configuration caching deep dive](https://medium.com/androiddevelopers/configuration-caching-deep-dive-bcb304698070)and the Gradle documentation about the[configuration cache](https://docs.gradle.org/current/userguide/configuration_cache.html).

#### Configuration cache issues introduced in Gradle 8.1 and Android Gradle Plugin 8.1

The configuration cache became stable in Gradle 8.1, and introduced file API tracking. Calls such as`File.exists()`,`File.isDirectory()`and`File.list()`are recorded by Gradle to track configuration input files.

Android Gradle Plugin (AGP) 8.1 uses these`File`APIs for some files that Gradle should not consider to be cache inputs. This triggers additional cache invalidation when used with Gradle 8.1 and higher, slowing build performance. The following are treated as cache inputs in AGP 8.1:

|                     Input                     |                         Issue Tracker                         | Fixed in |
| $GRADLE_USER_HOME/android/FakeDependency.jar  | [Issue #289232054](https://issuetracker.google.com/289232054) | AGP 8.2  |
|                 cmake output                  | [Issue #287676077](https://issuetracker.google.com/287676077) | AGP 8.2  |
| $GRADLE_USER_HOME/.android/analytics.settings | [Issue #278767328](https://issuetracker.google.com/278767328) | AGP 8.3  |
|-----------------------------------------------|---------------------------------------------------------------|----------|

If you use these APIs or a plugin that uses these APIs, you might experience a regression in your build time, because some build logic using these APIs can trigger additional cache invalidation. Please see[Improvements in the build configuration input tracking](https://blog.gradle.org/improvements-in-the-build-configuration-input-tracking)for a discussion of these patterns and how to fix the build logic, or temporarily disable the file API tracking.