Skip to content

Most visited

Recently visited

navigation

Optimize Your Build Speed

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

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

  1. Optimize your build configuration by taking a few steps that immediately benefit most Android Studio projects.
  2. 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.

Keep your tools up to date

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

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 desnity 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:

// 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);

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. To prevent Crashlytics from constantly 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. If the values in your manifest file or your app resources need to update with every build, then Instant Run cannot perform a code swap—it must build and install a new APK.

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 (which is not supported by Instant Run).
        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:

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

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

Enable configuration on demand

For Gradle to know exactly how to build your app, the build system configures all modules in your project, and their dependencies, before every build (even if you are building and testing only a single module). This slows down the build process for large multi-module projects. To tell Gradle to configure only the modules that you want to build, enable configuration on demand by following these steps:

  1. Open the Preferences window by clicking File > Settings (on Mac, Android Studio > Preferences).
  2. In the left pane, click Build, Execution, Deployment > Compiler.
  3. Check the Configure on demand checkbox.
  4. Click Apply or OK.

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 configuration on demand and parallel project execution more effective (when you enable those features).

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 Instant Run

Instant Run significantly reduces the time it takes to update your app by pushing certain code and resource changes without building a new APK—and, in some cases, without even restarting the current activity. Use Instant Run by clicking Apply Changes after making changes to your app, and it's enabled by default when you do the following:

If you don't see the Apply Changes button in the toolbar after meeting these requirements, make sure that Instant Run isn't disabled in the IDE settings by following these steps:

  1. Open the Settings or Preferences dialog.
  2. Navigate to Build, Execution, Deployment > Instant Run.
  3. Make sure Enable Instant Run is checked.

Enable 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.

Disable annotation processors

Incremental Java compilation is disabled when using annotation processors. If possible, try to avoid using annotation processors in order to benefit from compiling only the classes you modify between builds.

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.

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.

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

  1. With your project open in Android Studio, select View > Tool Windows > Terminal to open a command line at the root of your project.
  2. 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
    
  3. Execute a debug build of one of your product flavors, such as the "dev" flavor, with the following flags:
    gradlew --profile --recompile-scripts --offline --rerun-tasks assembleFlavorDebug
    
    • --profile: Enables profiling.
    • --recompile-scripts: Forces scripts to be recompiled while bypassing caching.
    • --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.
    • --rerun-tasks: Forces Gradle to rerun all tasks and ignore any task optimizations.
  4. After the build completes, use the Project window navigate to the project-root/build/reports/profile/ directory (as shown in figure 1).

    Figure 1. Project view indicating the location of profile reports.

  5. 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.

  6. 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 using ProGuard to remove unused code and shrink the APK size. However, smaller projects may benefit more from disabling ProGuard 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.

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience. (Dec 2017 Android Platform & Tools Survey)