构建时间过长会减慢您的开发流程。此页面提供了一些技巧来帮助解决构建速度瓶颈。
改进应用构建速度的常规流程如下
在开发应用时,尽可能部署到运行 Android 7.0 (API 级别 24) 或更高版本的设备。更新版本的 Android 平台实现了更好的应用更新机制,例如 Android 运行时 (ART) 和对 多个 DEX 文件 的原生支持。
注意:在您首次执行清理构建后,您可能会注意到随后的构建(清理构建和增量构建)即使没有使用此页面上描述的任何优化,也执行得快得多。这是因为 Gradle 守护程序有一个“预热”期,在此期间性能会逐渐提高——类似于其他 JVM 进程。
优化您的构建配置
遵循以下提示,以提高 Android Studio 项目的构建速度。
保持工具更新
Android 工具几乎每次更新都会收到构建优化和新功能。此页面上的一些提示假设您使用的是最新版本。为了利用最新的优化,请保持以下工具更新
使用 KSP 而不是 kapt
Kotlin 注解处理工具 (kapt) 比 Kotlin 符号处理器 (KSP) 慢得多。如果您正在编写带注释的 Kotlin 源代码,并使用支持 KSP 的工具(例如 Room)来处理注释,则需要 迁移到 KSP。
避免编译不必要的资源
避免编译和打包您未测试的资源,例如额外的语言本地化和屏幕密度资源。相反,只为您的“开发”风味指定一种语言资源和一种屏幕密度,如以下示例所示
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()
存储库中找到。但是,您的构建可能需要使用 gradlePluginPortal()
服务解析的第三方插件。
Gradle 按声明顺序搜索存储库,因此如果首先列出的存储库包含大多数插件,则构建性能会得到提高。因此,请通过将 gradlePluginPortal()
条目放在 settings.gradle
文件中的存储库块中的最后位置来对其进行实验。在大多数情况下,这会最大限度地减少冗余插件搜索的数量,并提高构建速度。
有关 Gradle 如何导航多个存储库的更多信息,请参阅 Gradle 文档中的 声明多个存储库。
在调试构建中使用静态构建配置值
始终使用静态值来表示调试构建类型中清单文件或资源文件中的属性。
使用动态版本代码、版本名称、资源或任何其他更改清单文件的构建逻辑,每次运行更改时都需要进行完整的应用程序构建,即使实际更改可能只需要热交换。如果您的构建配置需要此类动态属性,则将这些属性隔离到您的发布构建变体,并保持调试构建的静态值,如下面的示例所示
... // 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)。WebP 可以提供比 JPEG 或 PNG 更好的压缩效果。
减少图像文件大小而无需执行构建时压缩可以加快构建速度,尤其是在您的应用程序使用大量图像资源的情况下。但是,您可能会注意到设备 CPU 使用率略有增加,因为解压缩 WebP 图像。
禁用 PNG 压缩
如果您没有 将 PNG 图像转换为 WebP,您仍然可以通过禁用每次构建应用程序时自动图像压缩来加快构建速度。
如果您使用的是 Android Gradle 插件 3.0.0 或更高版本,则默认情况下会为“调试”构建类型禁用 PNG 压缩。要禁用针对其他构建类型的此优化,请将以下内容添加到您的 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 } } }
由于构建类型或产品风味不会定义此属性,因此您需要在构建应用程序的发布版本时手动将此属性设置为 true
。
试验 JVM 并行垃圾收集器
通过配置 Gradle 使用的最佳 JVM 垃圾收集器可以提高构建性能。虽然 JDK 8 默认配置为使用并行垃圾收集器,但 JDK 9 及更高版本配置为使用 G1 垃圾收集器。
为了潜在地提高构建性能,我们建议 使用并行垃圾收集器测试您的 Gradle 构建。在 gradle.properties
中设置以下内容
org.gradle.jvmargs=-XX:+UseParallelGC
如果该字段中已设置了其他选项,请添加一个新选项
org.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC
要衡量不同配置下的构建速度,请参阅 分析您的构建。
增加 JVM 堆大小
如果您发现构建速度很慢,尤其是垃圾收集在您的 构建分析器 结果中花费了超过 15% 的构建时间,那么您应该增加 Java 虚拟机 (JVM) 堆大小。在 gradle.properties
文件中,将限制设置为 4、6 或 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
类 可以为具有多个模块的应用程序提供更快的构建。这样做有助于通过确保每个模块的 R
类仅包含对自身资源的引用来防止资源重复,而不会从其依赖项中提取引用。这将导致更快的构建和相应的编译避免优势。这是 Android Gradle 插件 8.0.0 及更高版本的默认行为。
从 Android Studio Bumblebee 开始,非传递 R
类默认情况下对新项目启用。对于使用早期版本的 Android Studio 创建的项目,请将它们更新为使用非传递 R
类,方法是转到 **重构 > 迁移到非传递 R 类**。
要了解有关应用程序资源和 R
类的更多信息,请参阅 应用程序资源概述。
使用非常量 R 类
在应用程序和测试中使用非常量 R
类 字段,以提高 Java 编译的增量性,并允许更精确地缩减资源。对于库,R
类字段始终不是常量,因为资源在为依赖该库的应用程序或测试打包 APK 时被编号。这是 Android Gradle 插件 8.0.0 及更高版本的默认行为。
禁用 Jetifier 标志
由于大多数项目直接使用 AndroidX 库,因此您可以删除 Jetifier 标志以提高构建性能。要删除 Jetifier 标志,请在您的 gradle.properties
文件中设置 android.enableJetifier=false
。
构建分析器可以执行检查以查看是否可以安全地删除该标志,从而使您的项目具有更好的构建性能并从未维护的 Android 支持库迁移。要了解有关构建分析器的更多信息,请参阅 疑难解答构建性能。
使用配置缓存
配置缓存 允许 Gradle 记录有关构建任务图的信息并在后续构建中重用它,因此 Gradle 不必再次重新配置整个构建。
要启用配置缓存,请执行以下步骤
- 检查所有项目插件是否兼容。
使用 构建分析器 检查您的项目是否与配置缓存兼容。构建分析器运行一系列测试构建,以确定是否可以在项目中启用该功能。请参阅 问题 #13490,了解支持的插件列表。
将以下代码添加到
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 的某些构建逻辑可能会触发额外的缓存失效。请参阅 构建配置输入跟踪的改进,以详细了解这些模式以及如何修复构建逻辑,或者暂时禁用文件 API 跟踪。