优化您的构建速度

构建时间太长会拖慢您的开发过程。本页将介绍一些有助于解决构建速度瓶颈的技巧。

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

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

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

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

优化 build 配置

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

保持工具处于最新状态

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

使用 KSP 而非 kapt

Kotlin 注解处理工具 (kapt) 的运行速度明显慢于 Kotlin Symbol Processor (KSP)。如果您要编写带注解的 Kotlin 源代码,并使用支持 KSP 的注解工具(如 Room),则需要迁移到 KSP

避免编译不必要的资源

避免编译和打包不测试的资源(例如,其他语言本地化和屏幕密度资源)。您可以仅为“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 才能使用动态版本代码、版本名称、资源或可更改清单文件的任何其他构建逻辑,即使实际更改可能仅需要 1 次热交换也是如此。如果您的 build 配置需要此类动态属性,请将这些属性隔离到您的发布 build 变体中,并使相应值对您的调试 build 保持静态,如下例所示:

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

如需了解如何在项目中设置动态版本代码,请参阅 GitHub 上的 setVersionsFromTask 配方

使用静态依赖项版本

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 格式可以提供更好的压缩效果。

减小图片文件大小可以加快构建速度(无需在构建时进行压缩),尤其是当应用使用大量图片资源时。不过,在解压缩 WebP 图片时,您可能会注意到设备的 CPU 使用率有小幅上升。通过使用 Android Studio,您可以轻松地将图片转换为 WebP 格式

停用 PNG 处理

即使您不将 PNG 图片转换为 WebP 格式,仍然可以在每次构建应用时停用自动图片压缩,以加快构建速度。

如果您使用的是 Android Gradle 插件 3.0.0 或更高版本,则系统会在默认情况下针对“调试”编译类型停用 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

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

增加 JVM 堆大小

如果您发现构建速度较慢(尤其是在 Build Analyzer 结果中发现构建时间超时 15% 的情况),则应增加 Java 虚拟化机器 (JVM) 堆大小。 在 gradle.properties 文件中,将限制设置为 4 GB、6 GB 或 8 GB,如以下示例所示:

org.gradle.jvmargs=-Xmx6g

然后测试构建速度是否有提升。确定最佳堆大小最简单的方法是增加限额,然后测试是否有足够的构建速度提升效果。

如果您还使用 JVM 并行垃圾回收器,则整行命令应如下所示:

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

您可以通过开启 HeapDumpOnOutOfMemoryError 标记分析 JVM 内存错误。这样,JVM 会在内存耗尽时生成堆转储。

使用非传递 R 类

使用非传递 R可为具有多个模块的应用构建更快的 build。这样做有助于确保每个模块的 R 类仅包含对其自身资源的引用,而不会从其依赖项中提取引用,从而帮助防止资源重复。这样可以获得更快的 build,以及避免编译的相应优势。这是 Android Gradle 插件 8.0.0 及更高版本中的默认行为。

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

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

使用非常量 R 类

在应用和测试中使用非常量 R字段,以提高 Java 编译的增量并允许进行更精确的资源缩减。对于库,R 类字段始终不是常量,因为在为依赖于相应库的应用或测试打包 APK 时,资源会进行编号。这是 Android Gradle 插件 8.0.0 及更高版本中的默认行为。

停用 Jetifier 标志

由于大多数项目都直接使用 AndroidX 库,因此您可以移除 Jetifier 标志,以便获得更好的构建性能。如需移除 Jetifier 标志,请在 gradle.properties 文件中设置 android.enableJetifier=false

Build Analyzer 可以执行一项检查,确认是否可以安全移除该标记,使您的项目能够具有更好的构建性能,并不再使用未加维护的 Android 支持库。如需详细了解 Build Analyzer,请参阅排查构建性能问题

使用配置缓存

配置缓存允许 Gradle 记录有关构建任务图的信息,并在后续 build 中重复使用该任务图,因此 Gradle 无需再次重新配置整个 build。

如需启用配置缓存,请按以下步骤操作:

  1. 检查所有项目插件是否兼容。

    使用 Build Analyzer 检查项目是否与配置缓存兼容。Build Analyzer 会运行一系列测试 build,以确定是否可以为项目启用该功能。请参阅问题 13490,查看受支持的插件列表。

  2. 将以下代码添加到 gradle.properties 文件:

      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

启用配置缓存后,当您首次运行项目时,构建输出会显示 Calculating task graph as no configuration cache is available for tasks。在后续运行期间,构建输出会显示 Reusing configuration cache

如需详细了解配置缓存,请参阅配置缓存深度解析这篇博文和有关配置缓存的 Gradle 文档。

Gradle 8.1 和 Android Gradle 插件 8.1 中引入的配置缓存问题

配置缓存在 Gradle 8.1 中变得稳定,并引入了文件 API 跟踪功能。Gradle 会记录 File.exists()File.isDirectory()File.list() 等调用,以跟踪配置输入文件。

Android Gradle 插件 (AGP) 8.1 使用这些 File API 处理 Gradle 不应将其视为缓存输入的某些文件。与 Gradle 8.1 及更高版本结合使用时,这会导致额外的缓存失效,从而降低构建性能。 以下各项在 AGP 8.1 中被视为缓存输入:

输入 问题跟踪器 已在以下版本中修复
$GRADLE_USER_HOME/android/FakeDependency.jar 问题 289232054 AGP 8.2
Cmake 输出 问题 287676077 AGP 8.2
$GRADLE_USER_HOME/.android/analytics.settings 问题 278767328 AGP 8.3

如果您在使用这些 API 或使用这些 API 的插件,构建时间可能会降低,因为使用这些 API 的某些构建逻辑可能会触发额外的缓存失效操作。请参阅 build 配置输入跟踪的改进,了解这些模式的讨论以及如何修复构建逻辑或暂时停用文件 API 跟踪。