虽然 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 插件

Android Studio 用户应使用最新的 Android Gradle 插件,以确保兼容性并受益于新版本中的性能改进。

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

Java 和 Kotlin 编译

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

注解处理器和 Kotlin

上面针对纯 Java 项目的建议也适用于 Android 项目。但是,如果您将注解处理器(例如 Dagger2 或 Butterknife)与 Kotlin 和 kotlin-kapt 插件结合使用,您应该知道在 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)从 Android Gradle 插件 3.6.0 开始也支持缓存。

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

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

Lint

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

Fabric 插件和 Crashlytics

Fabric 插件用于集成 Crashlytics 崩溃报告工具(以及其他工具),非常流行,但在构建过程中会带来一些巨大的性能损失。这是因为您的应用程序的每个版本都需要有一个唯一的标识符,以便在 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")