优化构建速度

构建时间太长会拖慢您的开发过程。本页将介绍一些可以帮助您突破构建速度瓶颈的技巧。

提高构建速度的一般过程如下:

  1. 采取一些对大多数 Android Studio 项目立竿见影的措施,优化 build 配置
  2. 对构建进行性能剖析,确定并诊断一些对您的项目或工作站来说比较棘手的瓶颈问题。

开发应用时,您应尽可能将其部署到搭载 Android 7.0(API 级别 24)或更高版本的设备中。较新版本的 Android 平台有更出色的机制来向您的应用推送更新,例如 Android 运行时 (ART) 以及对多个 DEX 文件的原生支持。

注意:您完成首次干净构建后,可能会注意到后续构建(干净和增量)的执行速度明显加快了(即使您没有使用本页面介绍的任何优化措施)。这是因为 Gradle 守护程序有一个性能提升“预热”期,类似于其他 JVM 进程。

优化 build 配置

按照下面的提示操作,以提高 Android Studio 项目的构建速度。

确保工具已是最新版本

几乎每次更新时,Android 工具都会获得构建方面的优化和新功能,本页介绍的一些提示假设您使用的是最新版本。为了充分利用最新的优化措施,请确保以下工具始终是最新版本:

避免编译不必要的资源

避免编译和打包不测试的资源(例如,其他语言本地化和屏幕密度资源)。为此,您可以仅为“dev”变种的版本指定一个语言资源和屏幕密度,如下面的示例中所示:

Groovy

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

Kotlin

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

尝试将 Gradle 插件门户放置在最后

在 Android 中,所有插件都位于 google()mavenCentral() 代码库中。不过,build 可能需要使用 gradlePluginPortal() 服务解析的第三方插件。Gradle 会按照声明的顺序搜索代码库,因此,如果先列出的代码库包含大多数插件,build 性能就会得到提升。因此,您可以尝试将 gradlePluginPortal() 条目放置在 settings.gradle 文件的代码库中最靠后的位置。在大多数情况下,这样可以最大限度地减少冗余插件搜索次数,并提高构建速度。

如需详细了解 Gradle 如何导航多个代码库,请参阅 Gradle 文档中的声明多个代码库

将静态 build 配置值用于调试 build

对于那些会出现在调试 build 类型的清单文件或资源文件中的属性,应始终使用静态/硬编码值。

例如,您每次需要运行更改时,要使用动态版本代码、版本名称、资源或可以更改清单文件的任何其他构建逻辑,都需要完整的应用 build,即使实际更改可能仅需要一次热交换,也是如此。如果您的 build 配置需要此类动态属性,请将其隔离到您的发布 build 变体中,并使该值对您的调试 build 保持静态。有关示例,请参阅下面的 build.gradle 文件。

Groovy

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 app 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 app, 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;
        }
    }
}

Kotlin

val MILLIS_IN_MINUTE = 1000 * 60
val minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE

android {
    ...
    defaultConfig {
        // Making either of these two values dynamic in the defaultConfig will
        // require a full app 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 app, 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.forEach { variant ->
        if (variant.buildType.name == "release") {
            variant.mergedFlavor.versionCode = minutesSinceEpoch
            variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName
        }
    }
}

使用静态依赖项版本

build.gradle 文件中声明依赖项时,您应当避免在结尾处使用带加号的版本号,例如 'com.android.tools.build:gradle:2.+'。使用动态版本号可能会导致意外的版本更新和难以解析版本差异,并会因 Gradle 检查有无更新而减慢构建速度。您应该使用静态/硬编码版本号。

创建库模块

在应用中查找可以转换成 Android 库模块的代码。以这种方式将您的代码模块化,可以让构建系统仅编译您修改的模块,并缓存输出以用于未来的构建。此外,这种方式也会让并行项目执行更有效(当您启用该优化时)。

为自定义构建逻辑创建任务

创建构建性能剖析报告后,如果性能剖析报告显示相当长的一部分构建时间用在了“配置项目”阶段,请检查 build.gradle 脚本并查找您可以添加到自定义 Gradle 任务中的代码。将某些构建逻辑移到任务中后,它仅会在需要时运行,可以缓存结果以用于后续构建,并且该构建逻辑将可以并行运行(如果您已启用并行项目执行)。如需了解详情,请阅读官方 Gradle 文档

提示:如果您的构建包含大量自定义任务,您可能需要通过创建自定义任务类来整理 build.gradle 文件。将您的类添加到 project-root/buildSrc/src/main/groovy/ 目录中,Gradle 会自动将其添加到项目中所有 build.gradle 文件的类路径中。

将图片转换为 WebP 格式

WebP 是一种既可以提供有损压缩(像 JPEG 一样)也可以提供透明度(像 PNG 一样)的图片文件格式,不过与 JPEG 或 PNG 相比,这种格式可以提供更好的压缩。减小图片文件大小可以加快构建速度(无需在构建时进行压缩),尤其是当应用使用大量图片资源时。不过,在解压缩 WebP 图片时,您可能会注意到设备的 CPU 使用率有小幅上升。通过使用 Android Studio,您可以轻松地将图片转换为 WebP 格式

停用 PNG 处理

如果您无法(或者不想)将 PNG 图片转换为 WebP 格式,可以在每次构建应用时停用自动图片压缩,从而提高构建速度。如果您使用的是 Android 插件 3.0.0 或更高版本,默认情况下仅针对“debug”build 类型停用 PNG 处理。如需针对其他 build 类型停用此优化,请将以下代码添加到 build.gradle 文件中:

Groovy

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

Kotlin

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

由于 build 类型或产品变种不定义此属性,因此在构建应用的发布版本时,您需要手动将此属性设置为 true

配置 JVM 垃圾回收器

通过配置 Gradle 所用的最佳 JVM 垃圾回收器,可以提升构建性能。虽然 JDK 8 默认配置为使用并行垃圾回收器,JDK 9 及更高版本已配置为使用 G1 垃圾回收器

为提高构建性能,我们建议您使用并行垃圾回收器测试 Gradle 构建。在 gradle.properties 中设置以下内容:

org.gradle.jvmargs=-XX:+UseParallelGC

如果此字段中已设置了其他选项,请添加一个新选项:

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

如需衡量采用不同配置时的构建速度,请参阅对构建进行性能剖析

使用非传递 R 类

您应使用非传递 R 类,以便让具有多个模块的应用获得更快的 build。这样做有助于确保每个模块的 R 类仅包含对其自身资源的引用,而不会从其依赖项中提取引用,从而帮助防止资源重复。这样可以获得更快的 build,以及避免编译的相应优势。

从 Android Studio Bumblebee 开始,新项目的非传递 R 类默认处于开启状态。对于使用早期版本的 Studio 创建的项目,您可以依次前往 Refactor > Migrate to Non-transitive R Classes,将项目更新为使用非传递 R 类。

如需详细了解应用资源和 R 类,请参阅应用资源概览

停用 Jetifier 标志

大多数项目都直接使用 AndroidX 库,因此您也许可以移除 Jetifier 标志,以便获得更好的构建性能。如需取消 Jetifier 检查,请在 gradle.properties 文件中设置 android.enableJetifier=false。Build Analyzer 可以执行一项检查,确认是否可以安全移除该标志,使您的项目能够具有更好的构建性能,并不再使用未加维护的 Android 支持库。如需详细了解 Build Analyzer,请参阅排查构建性能问题

使用配置缓存(实验性功能)

配置缓存是一项实验性功能,可让 Gradle 记录有关构建任务图的信息,并在后续 build 中重复使用该任务图,而不必再次重新配置整个 build。如需启用配置缓存,请按以下步骤操作:

  1. 检查所有项目插件是否兼容。使用 Build Analyzer 检查项目是否与配置缓存兼容。它会运行一系列测试 build,以确定是否可以为项目启用该功能。另请参阅问题 13490,查看受支持的插件列表。
  2. 将以下代码添加到 gradle.properties 文件:

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

  3. 启用配置缓存后,当您首次运行项目时,构建输出应显示 Calculating task graph as no configuration cache is available for tasks。在后续运行期间,构建输出应显示 Reusing configuration cache
如需详细了解配置缓存,请参阅配置缓存深度解析这篇博文和有关配置缓存的 Gradle 文档