缓存 Android 项目
尽管 Android 使用 Java Toolchain 作为基础,但与纯 Java 项目仍有一些显著差异;这些差异会影响 Task 的可缓存性。对于包含 Kotlin 源代码(并因此使用 kotlin-android
插件)的 Android 项目来说,这一点尤其如此。
消歧
本指南是关于 Gradle 的构建缓存,但您可能也听说过 Android 构建缓存。它们是不同的东西。Android 缓存是 Android 插件中某些 Task 的内部缓存,最终将被移除,转而支持原生的 Gradle 功能。
为什么要使用构建缓存?
构建缓存可以显著提高 Android 项目的构建性能,在许多情况下可以提高 30-40%。Android Gradle 插件提供的许多编译和组装 Task 都是可缓存的,并且随着每次新迭代,越来越多的 Task 也变得可缓存。
更快的 CI 构建
CI 构建尤其受益于构建缓存。典型的 CI 构建以 clean
开始,这意味着现有的构建输出将被删除,并且构成构建的任何 Task 都不会是 UP-TO-DATE
。然而,很可能许多 Task 在之前的 CI 构建中已经以完全相同的输入运行过,从而填充了构建缓存;可以安全地重用之前运行的输出,从而显著提高构建性能。
将 CI 构建结果用于本地开发
一天开始工作时,您的第一个 Task 通常是拉取 main 分支,然后运行构建(无论您是否要求,Android Studio 可能都会这样做)。假设所有合并到 main 的更改都在 CI 上构建过(这是一个最佳实践!),那么当您运行当天的第一个本地构建时,可以通过 Gradle 的远程缓存获得比通常更大的益处。CI 已经构建过这个提交了——为什么您还需要重复这项工作呢?
Android Gradle 插件
Android Studio 用户应使用最新的 Android Gradle 插件,以确保兼容性并受益于新版本中的性能改进。
优化构建时,您首先应该始终确保使用的是最新稳定支持版本的 Android Gradle 插件和 Gradle 构建工具。在撰写本文时,它们分别是 3.3.0 和 5.0。这些工具的每个新版本都包含许多性能改进,其中最重要的一项就是针对构建缓存。
Java 和 Kotlin 编译
上文“缓存 Java 项目”中关于讨论在这里同样适用,但需要注意,对于包含 Kotlin 源代码的项目,Kotlin 编译器目前不像 Java 编译器那样支持避免编译(compile avoidance)。
注解处理器和 Kotlin
上文针对纯 Java 项目的建议也适用于 Android 项目。但是,如果您将注解处理器(例如 Dagger2 或 Butterknife)与 Kotlin 和 kotlin-kapt 插件结合使用,您应该知道在 Kotlin 1.3.30 之前,kapt 默认不被缓存。
您可以选择启用它(推荐做法),通过将以下内容添加到构建脚本中:
pluginManager.withPlugin("kotlin-kapt") {
configure<KaptExtension> { useBuildCache = true }
}
plugins.withId("kotlin-kapt") {
kapt.useBuildCache = true
}
单元测试执行
与纯 Java 项目中的单元测试类似,Android 项目中等效的测试 Task (AndroidUnitTest
) 自 Android Gradle Plugin 3.6.0 起也变得可缓存。
插桩测试执行(即 Espresso 测试)
Android 插桩测试 (DeviceProviderInstrumentTestTask
),通常被称为“Espresso”测试,也是不可缓存的。Google Android 团队也正在努力使这类测试可缓存。请参见此问题。
Lint
Android 的 Lint
Task 用户深知使用它所付出的沉重性能代价,但也知道它对于发现 Android 项目中的常见问题不可或缺。目前,此 Task 不可缓存。计划在 Android Gradle Plugin 3.5 发布后使其可缓存。这也是始终使用最新版 Android 插件的另一个原因!
Fabric 插件和 Crashlytics
Fabric 插件用于集成 Crashlytics 崩溃报告工具(以及其他工具),它非常流行,但在构建过程中会带来一些严重的性能损失。这是因为需要为应用的每个版本提供唯一的标识符,以便在 Crashlytics 控制面板中进行识别。实际上,Crashlytics 的默认行为是将“每个版本”等同于“每个构建”。这会使得增量构建失效,因为每个构建都是唯一的。出于同样的原因,它还会破坏构建中某些 Task 的可缓存性。这可以通过简单地在“debug”构建中禁用 Crashlytics 来解决。您可以在Crashlytics 文档中找到相关说明。
如果您使用的是 Kotlin DSL,引用文档中描述的修复方法无法直接生效;请参阅下文了解变通方法。 |
Kotlin DSL
如果您使用的是 Kotlin DSL,引用文档中描述的修复方法无法直接生效;这是由于 Kotlin DSL 与 Fabric 插件不兼容造成的。有一个简单的变通方法可以解决此问题,该方法基于 Kotlin DSL 入门中的此建议。
在应用 io.fabric
插件的模块中创建文件 fabric.gradle
。此文件(称为脚本插件)应包含以下内容:
plugins.withId("com.android.application") { // or "com.android.library" android.buildTypes.debug.ext.enableCrashlytics = false }
然后,在该模块的 build.gradle.kts
文件中应用此脚本插件:
apply(from = "fabric.gradle")