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

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

Optimize your build configuration by taking a few steps that immediately benefit most Android Studio projects. 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, you should 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) and native support for multiple DEX files.

Note: After your first clean build, you may notice that subsequent builds—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.

The Android tools receive build optimizations and new features with almost every update, and 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:

Create a build variant for development

Many of the configurations you need when preparing your app for release are not required while developing your app. Enabling unnecessary build processes slows down your incremental and clean builds, so configure a build variant that keeps only the build configurations you need while developing your app. The following sample creates a "dev" flavor and a "prod" flavor (for your release version configurations):

android { ... defaultConfig {...} buildTypes {...} productFlavors { // When building a variant that uses this flavor, the following configurations // override those in the defaultConfig block. dev { // To avoid using legacy multidex when building from the command line, // set minSdkVersion to 21 or higher. When using Android Studio 2.3 or higher, // the build automatically avoids legacy multidex when deploying to a device running // API level 21 or higher—regardless of what you set as your minSdkVersion. minSdkVersion 21 versionNameSuffix "-dev" applicationIdSuffix '.dev' } prod { // If you've configured the defaultConfig block for the release version of // your app, you can leave this block empty and Gradle uses configurations in // the defaultConfig block instead. You still need to create this flavor. // Otherwise, all variants use the "dev" flavor configurations. } } }

If your build configuration already uses product flavors to create different versions of your app, you can combine the "dev" and "prod" configurations with those flavors by using flavor dimensions. For example, if you already configure a "demo" and "full" flavor, you can use the following sample configuration to create combined flavors, such as "devDemo" and "prodFull":

android { ... defaultConfig {...} buildTypes {...} // Specifies the flavor dimensions you want to use. The order in which you // list each dimension determines its priority, from highest to lowest, // when Gradle merges variant sources and configurations. You must assign // each product flavor you configure to one of the flavor dimensions. flavorDimensions "stage", "mode" productFlavors { dev { dimension "stage" minSdkVersion 21 versionNameSuffix "-dev" applicationIdSuffix '.dev' ... } prod { dimension "stage" ... } demo { dimension "mode" ... } full { dimension "mode" ... } } }

Enable single-variant project sync

Syncing your project with your build configuration is an important step in letting Android Studio understand how your project is structured. However, this process can be time-consuming for large projects. 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. The optimization is enabled by default on all projects.

To enable this optimization manually, click File > Settings > Experimental > Gradle (Android Studio > Preferences > Experimental > Gradle on a Mac) and select the Only sync the active variant checkbox.

Note: This optimization fully supports projects that include Java and C++ languages, and has some support for Kotlin. When enabling the optimization for projects with Kotlin content, Gradle sync falls back to using full variants internally.

Avoid compiling unnecessary resources

Avoid compiling and packaging resources that you aren't testing (such as additional language localizations and screen-density resources). You can do that by only specifying one language resource and screen density for your "dev" flavor, as shown in the following sample:

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

Disable Crashlytics for your debug builds

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 } }

You also need to disable the Crashlytics kit at runtime for debug builds by changing the way you initialize support for Fabric in your app, as shown below:

Kotlin // Initializes Fabric for builds that don't use the debug build type. Crashlytics.Builder() .core(CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()) .build() .also { crashlyticsKit -> Fabric.with(this, crashlyticsKit) } Java // Initializes Fabric for builds that don't use the debug build type. Crashlytics crashlyticsKit = new Crashlytics.Builder() .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()) .build(); Fabric.with(this, crashlyticsKit);

Disable automatic build ID generation

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. Because this build ID is stored in a resource file that is referenced by the manifest, disabling automatic build ID generation also allows you to use Apply Changes alongside Crashlytics for your debug builds.

To prevent Crashlytics from automatically updating its build ID, add the following to your build.gradle file:

android { ... buildTypes { debug { ext.alwaysUpdateBuildId = false } }

For more information about optimizing your builds while using Crashlytics, read the official documentation.

Use static build config values with your debug build

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

For example, using dynamic version codes, version names, resources, or any other build logic that changes the manifest file requires a full APK build every time you want to run a change—even though the actual change might otherwise require only a hot swap. If your build configuration requires such dynamic properties, then isolate them to your release build variants and keep the values static for your debug builds, as shown in the build.gradle file below.

int MILLIS_IN_MINUTE = 1000 * 60 int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE android { ... defaultConfig { // Making either of these two values dynamic in the defaultConfig will // require a full APK build and reinstallation because the AndroidManifest.xml // must be updated. versionCode 1 versionName "1.0" ... } // The defaultConfig values above are fixed, so your incremental builds don't // need to rebuild the manifest (and therefore the whole APK, slowing build times). // But for release builds, it's okay. So the following script iterates through // all the known variants, finds those that are "release" build types, and // changes those properties to something dynamic. applicationVariants.all { variant -> if (variant.buildType.name == "release") { variant.mergedFlavor.versionCode = minutesSinceEpoch; variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName; } } }

Use static dependency versions

When you declare dependencies in your build.gradle files, you should avoid using version numbers 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. You should use static/hard-coded version numbers instead.

Enable offline mode

If you are on a slow network connection, your build times may suffer when Gradle attempts to use network resources to resolve dependencies. You can tell Gradle to avoid using network resources by using only the artifacts that it has cached locally.

To use Gradle offline when building with Android Studio, proceed as follows:

Open the Preferences window by clicking File > Settings (on Mac, Android Studio > Preferences). In the left pane, click Build, Execution, Deployment > Gradle. Check the Offline work checkbox. Click Apply or OK.

If you're building from the command line, pass the --offline option.

Create library modules

Look for code in your app 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).

Create tasks for custom build logic

After you create a build profile, if it 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 that you can include in a custom Gradle task. By moving some build logic into a task, it is run 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). To learn more, read the official Gradle documentation.

Tip: If your build includes a large number of custom tasks, you may want to declutter your build.gradle files by creating custom task classes. Add you classes to the project-root /buildSrc/src/main/groovy/ directory and Gradle automatically includes them in the classpath for all build.gradle files in your project.

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, 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. Using Android Studio, you can easily convert your images to WebP.

Disable PNG crunching

If you can't (or don't want to) convert your PNG 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 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 } } // If you're using an older version of the plugin, use the // following: // aaptOptions { // cruncherEnabled 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.

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 because the build system can simply reuse those cached files during subsequent builds, instead of recreating them.

New projects that use Android plugin 2.3.0 and higher enable the build cache by default (unless you explicitly disable the build cache). To learn more, read Accelerate clean builds with build cache.

Use incremental annotation processors

Android Gradle plugin 3.3.0 and higher improve support for incremental annotation processing. So, to improve incremental build speeds, you should update your Android Gradle plugin and use only incremental annotation processors whenever possible.

Note: This feature is compatible with Gradle versions 4.10.1 and higher, except for Gradle 5.1 (see Gradle issue #8194).

To get started, see the following list of popular annotation processors that support incremental annotation processing. For a more complete list, see State of support in popular annotation processors. Some of the annotation processors may require additional steps to enable the optimization, so make sure to read documentation for each annotation processor.

Additionally, if you use Kotlin in your app, you need to use kapt 1.3.30 and higher to support incremental annotation processors for your Kotlin code. Make sure to read the official documentation on whether you need to manually enable this behavior.

Keep in mind, if you have to use one or more annotation processors that don't support incremental builds, annotation processing won't be incremental. However, if your project is using kapt, Java compilation is still incremental.

Profile your build

Larger projects, or those that implement a lot of custom build logic, may require you to take a deeper look into the build process to find bottlenecks. You can do that by profiling how long Gradle takes to execute each phase of the build lifecycle and each build task. For example, if your build profile shows that Gradle is spending too much time configuring your project, it may suggest that you need to move custom build logic out of the configuration phase. Additionally, if the mergeDevDebugResources task consumes a large amount of the build time, it may indicate that you need to either convert your images to WebP or disable PNG crunching.

If you're using Android Studio 4.0 or higher, the best way to investigate build performance issues is by using the Build Analyzer.

Profile your build from the command line

If you're not using Android Studio, troubleshooting your build speed typically involves running your build from the command line with profiling enabled, making some tweaks to your build configuration, and profiling some more to observe the results of your changes.

To generate and view a build profile, perform the following steps:

Open a commandline terminal at the root of your project. Perform a clean build by entering the following command. As you profile your build, you should perform a clean build between each build you profile because Gradle skips tasks when inputs to a task (such as source code) don't change. Thus, a second build with no input changes always runs faster because tasks are not being re-run. So running the clean task between your builds ensures that you profile the full build process. // On Mac or Linux, run the Gradle wrapper using "./gradlew". gradlew clean Execute a debug build of one of your product flavors, such as the "dev" flavor, with the following flags: gradlew --profile --offline --rerun-tasks assemble Flavor Debug --profile : Enables profiling.

: Enables profiling. --offline : Disables Gradle from fetching online dependencies. This makes sure than any delays caused by Gradle attempting to update your dependencies don't interfere with your profiling data. You should have already built your project once to make sure Gradle has already downloaded and cached your dependencies.

: Disables Gradle from fetching online dependencies. This makes sure than any delays caused by Gradle attempting to update your dependencies don't interfere with your profiling data. You should have already built your project once to make sure Gradle has already downloaded and cached your dependencies. --rerun-tasks : Forces Gradle to rerun all tasks and ignore any task optimizations. Figure 1. Project view indicating the location of profile reports. After the build completes, use the Project window navigate to the project-root /build/reports/profile/ directory (as shown in figure 1). Right-click the profile- timestamp .html file and select Open in Browser > Default. The report should look similar to the one shown in figure 2. You can inspect each tab in the report to learn about your build, such as the Task Execution tab which shows how long Gradle took to execute each build task. Figure 2. Viewing a report in a browser. Optional: Before making any changes to your project or build configuration, repeat the command in step 3, but omit the --rerun-tasks flag. Because Gradle attempts to save time by not re-executing tasks whose inputs haven't changed (these are indicated as UP-TO-DATE in the Task Execution tab of the report, as shown in figure 3), you can identify which tasks are performing work when they shouldn't be. For example, if the :app:processDevUniversalDebugManifest is not marked as UP-TO-DATE , it may suggest that your build configuration is dynamically updating the manifest with every build. However, some tasks need to run during each build, such as :app:checkDevDebugManifest . Figure 3. Viewing task execution results.

Now that you have a build profile report, you can begin looking for optimization opportunities by inspecting the information in each tab of the report. Some build settings require experimentation because the benefits may differ between projects and workstations. For example, projects with a large codebase may benefit from code shrinking to remove unused code and shrink the APK size. However, smaller projects may benefit more from disabling code shrinking altogether. Additionally, increasing the Gradle heap size (using org.gradle.jvmargs ) might negatively impact performance on low-memory machines.

After making a change to your build configuration, observe the results of your changes by repeating the steps above and generating a new build profile. For example, figure 4 shows a report for the same sample app after applying some of the basic optimizations described in this page.

Figure 4. Viewing a new report after optimizing the build speed.

Tip: For a more robust profiling tool, consider using Gradle's open-source profiler.