虽然 Android 以 Java 工具链为基础,但它与纯 Java 项目之间仍然存在一些重大差异;这些差异会影响任务的可缓存性。对于包含 Kotlin 源代码(因此使用 kotlin-android 插件)的 Android 项目来说,情况更是如此。

消歧义

本指南介绍的是 Gradle 的构建缓存,但您可能也听说过 Android 构建缓存。它们是不同的东西。Android 缓存是 Android 插件中某些任务的内部缓存,最终将被原生 Gradle 支持取代。

为什么要使用构建缓存?

构建缓存可以显著提高 Android 项目的构建性能,在许多情况下可以提高 30-40%。Android Gradle 插件提供的许多编译和组装任务都是可缓存的,并且随着每次新迭代,更多任务变得可缓存。

更快的 CI 构建

CI 构建特别受益于构建缓存。典型的 CI 构建从 clean 开始,这意味着预先存在的构建输出将被删除,并且构成构建的所有任务都不会是 UP-TO-DATE。但是,这些任务中的许多可能在之前的 CI 构建中使用完全相同的输入运行过,从而填充了构建缓存;这些先前运行的输出可以安全地重复使用,从而显着提高构建性能。

将 CI 构建重复用于本地开发

当你开始工作的那天登录时,你的第一个任务通常是拉取主分支,然后运行构建(Android Studio 可能会执行后者,无论你是否要求它)。假设所有合并到主分支的代码都在 CI 上构建(最佳实践!),你可以预期当天第一次本地构建将从 Gradle 的远程缓存中获得比平时更大的收益。CI 已经构建了这个提交——为什么要重新做这项工作呢?

切换分支

在本地开发期间,每天切换几次分支并不罕见。这会破坏 增量构建(即 UP-TO-DATE 检查),但这个问题可以通过使用本地构建缓存来缓解。你可能会在分支 A 上运行构建,这将填充本地缓存。然后你切换到分支 B 以进行代码审查、帮助同事或解决开放 PR 的反馈。然后你切换回分支 A 以继续你最初的工作。当你下次构建时,之前在分支 A 上工作时构建的所有输出都可以从缓存中重复使用,从而节省了大量时间。

Android Gradle 插件和 Gradle 构建工具

在优化构建时,你应该始终做的第一件事是确保你使用的是 Android Gradle 插件和 Gradle 构建工具的最新稳定支持版本。在撰写本文时,它们分别是 3.3.0 和 5.0。这些工具的每个新版本都包含许多性能改进,其中最重要的是对构建缓存的改进。

Java 和 Kotlin 编译

上面“缓存 Java 项目”中关于 讨论 的内容在这里同样适用,但需要注意的是,对于包含 Kotlin 源代码的项目,Kotlin 编译器目前不支持 编译避免,而 Java 编译器则支持。

注解处理器和 Kotlin

针对纯 Java 项目的 上述建议 也适用于 Android 项目。但是,如果您在使用 Kotlin 和 kotlin-kapt 插件时使用注解处理器(例如 Dagger2 或 Butterknife),您应该知道在 Kotlin 1.3.30 之前,kapt 默认情况下不会被缓存

您可以选择启用它(建议这样做),方法是在构建脚本中添加以下内容

build.gradle.kts
pluginManager.withPlugin("kotlin-kapt") {
    configure<KaptExtension> { useBuildCache = true }
}
build.gradle
plugins.withId("kotlin-kapt") {
    kapt.useBuildCache = true
}

单元测试执行

与纯 Java 项目中的单元测试不同,Android 项目中的等效测试任务(AndroidUnitTest)不可缓存。Google 团队正在努力使这些测试可缓存。请参阅 此问题

仪器测试执行(即 Espresso 测试)

Android 仪器测试(DeviceProviderInstrumentTestTask),通常称为“Espresso”测试,也不可缓存。Google Android 团队也正在努力使此类测试可缓存。请参阅 此问题

Lint

Android 的 Lint 任务的用户都知道使用它会带来巨大的性能损失,但也知道它对于在 Android 项目中查找常见问题是必不可少的。目前,此任务不可缓存。计划在 Android Gradle 插件 3.5 发布时使此任务可缓存。这也是始终使用最新版本的 Android 插件的另一个理由!

Fabric 插件和 Crashlytics

用于集成 Crashlytics 崩溃报告工具(以及其他工具)的 Fabric 插件非常流行,但在构建过程中会带来一些沉重的性能损失。这是因为需要为您的应用程序的每个版本提供唯一的标识符,以便它可以在 Crashlytics 仪表板中识别。实际上,Crashlytics 的默认行为是将“每个版本”等同于“每个构建”。这会破坏 增量构建,因为每个构建都是唯一的。它还会破坏构建中某些任务的可缓存性,原因相同。这可以通过简单地在“调试”构建中禁用 Crashlytics 来解决。您可以在 Crashlytics 文档 中找到相关说明。

如果您使用的是 Kotlin DSL,则参考文档中描述的修复方法无法直接使用;请参阅下面的解决方法。

Kotlin DSL

如果您使用的是 Kotlin DSL,则参考文档中描述的修复方法无法直接使用;这是由于该 Kotlin DSL 与 Fabric 插件之间存在不兼容性。有一个简单的解决方法,基于 Kotlin DSL 入门指南中的建议

在您应用 io.fabric 插件的模块中创建一个名为 fabric.gradle 的文件。此文件(称为脚本插件)应包含以下内容

fabric.gradle
plugins.withId("com.android.application") { // or "com.android.library"
    android.buildTypes.debug.ext.enableCrashlytics = false
}

然后,在模块的 build.gradle.kts 文件中,应用此脚本插件

build.gradle.kts
apply(from = "fabric.gradle")