升级依赖版本

升级依赖项可让您访问其最新功能、错误修复和改进。要升级依赖项,您需要了解 Gradle 如何解析您请求的版本、涉及的风险以及您可以采取哪些步骤来降低这些风险。

考虑您的升级策略

任何升级最重要的步骤是风险分析。确定您对每个升级的依赖项的接受程度。定义升级策略时有许多需要考虑的因素,包括

构建库

您是正在构建一个用户下载并在设备上运行的应用?还是正在构建一个帮助其他开发者构建其应用的库?

如果您正在构建应用,您的重点应该是保持应用最新且稳定。

如果您正在构建库,您的重点应该放在其他开发者的应用上。您的升级会影响您的使用者。如果您升级某个依赖项,该版本会成为 Gradle 依赖项解析的候选版本,可能会破坏应用对该依赖项的使用。

首先,尽可能最小化库的依赖项。您的依赖项越少,对使用者依赖项解析的影响就越小。

请务必遵循语义化版本控制,以帮助表明您正在进行的更改类型。例如,AndroidX 遵循语义化版本控制并添加了预发布版本控制方案。尽量避免 major 版本升级,以避免破坏您的使用者。

考虑创建库的候选发布版本 (RC),供用户提前测试。

有关保持库的应用二进制接口 (ABI) 兼容的详细信息,请参阅适用于库作者的向后兼容性指南。使用集成测试和工具(如二进制兼容性验证器)来确保您的 ABI 更改与您预期的版本更改匹配。

如果您在库的较低版本的 patch 版本中发布修复程序,除非您的使用者想要新功能,否则他们无需将您的库升级到下一个 majorminor 版本。在这些升级中避免升级传递性依赖项。

如果您的库升级需要进行可能对您的使用者特别棘手的破坏性更改,请考虑将其作为新工件发布,以便旧版本和新版本可以共存并允许更渐进的发布。

注意:如果您的某个依赖项的升级包含主要的 API 更改,您可能需要在 majorminor 发布中升级它并进行任何必要的更改。如果您不这样做,您的库的用户可能会这样做,从而导致您的库与该依赖项之间出现不兼容。即使您的库没有更改,这也可能适用。您可以仅为了升级该依赖项而发布新版本。

发布周期

您多久发布一次您的应用或库?

更短的开发和发布周期

  • 升级的时间更少。
  • 您可能会很快落后。
  • 频繁的小升级可以减轻工作量。
  • 如果库升级出现问题,您可以更快地回滚该升级。
  • 如果您使用有助于自动化依赖项升级的插件或工具,请务必分析结果以检查风险。

更长的开发和发布周期

  • 有更多时间进行和测试升级。
  • 依赖项的较新版本更有可能在您的周期内发布。
  • 回滚升级以及发布您的应用或库需要更长时间。

及时了解最新功能

您更喜欢使用最新的可用功能和 API,还是仅在需要某个功能或错误修复时才升级?

考虑频繁升级的权衡。未来的升级更容易(需要集成的更改更少),但您承担升级风险的频率也更高。

测试库的预发布(alpha、beta、候选发布)版本的升级有助于在稳定版本可用时做好准备。

新依赖项

如果您要添加新的依赖项,请考虑采用严格的审查流程,检查该库是否符合所有风险标准,以确保它们已得到妥善评估。不要在未经审查的情况下添加新的依赖项。

专属团队

您是否有专属的构建团队?您的软件工程师维护构建吗?专属团队通常可以花费更多时间分析升级风险并测试新版本,以确保构建在工程师使用新版本之前正常工作。

升级类型

有些升级比其他升级更重要。思考哪些对您来说最重要。

构建工具升级(例如 Gradle 和 Gradle 插件)通常对用户的影响较小,并且大部分风险在于您内部的构建。构建本身有助于验证这些更改。库和 SDK 升级更难以验证,并且对您的用户构成更高的风险。

Android Gradle 插件 (AGP) — 用于构建您的 Android 应用或库的工具。这是您可以进行的最关键的升级,因为它通常包含或启用了性能改进、错误修复、新的 lint 规则以及对新 Android 平台版本的支持。

Gradle — 当您升级 AGP 或其他 Gradle 插件时,通常需要升级 Gradle。

其他 Gradle 插件 — 有时 Gradle 的插件 API 会发生变化。升级 Gradle 时,请检查您使用的插件是否有升级版本。

Kotlin 和 Java — 一些库和插件需要最低版本的 Kotlin 或 Java,或者您希望利用新的语言功能、API 或性能改进。

Android 平台 — Play 商店要求定期升级 Android SDK。您应该尽快测试 Android SDK 的新版本。某些 SDK 升级需要更改您的应用,例如新的权限或使用新的 API。

— 您是否希望根据库与您整体架构的紧密程度来确定优先级?

  • 平台和架构相关的库(例如 AndroidX)经常会更改以利用新功能或帮助抽象平台中的更改。至少在每次升级 Android 平台或其他架构相关库时升级这些库。
  • 其他库升级可以分散或延迟进行,除非您需要新功能或特定的错误修复。

Android Studio — 保持 Android Studio 更新可以让您访问底层 IntelliJ IDEA 平台的最新功能和错误修复,以及使用最新 Android SDK 的工具。

可用工具

如果您使用有助于自动化依赖项升级的插件或工具,请务必分析结果以检查风险。

特定类型升级的策略

升级某些类型的依赖项可能会产生级联效应,需要升级其他类型的依赖项。我们在工具和库的相互依赖性中讨论了构建元素之间的关系。

Build dependencies and their relationships
图 1. 构建关系。

升级每种类型的组件时,请考虑升级如何影响构建中的其他组件。

Android Gradle 插件 (AGP)

Android Studio 包含一个 AGP 升级助手,可以协助完成这些任务。

如果您使用助手或手动执行升级,请考虑以下事项

查看 AGP 版本说明

将 Gradle 升级到列出的版本或更高版本。

Android Studio 升级到支持所选 AGP 版本的版本。

使用支持您要使用的 Android SDK 的 Android Studio 和 AGP 版本。

检查与 SDK Build Tools、NDK 和 JDK 的兼容性。

如果您开发扩展或使用 AGP 数据的 Gradle 插件(内部或公共使用),请检查是否需要升级您的插件。有时 AGP 会弃用并稍后移除 API,从而导致与以前的插件不兼容。

Kotlin 编译器、语言和运行时

查看 Kotlin 版本说明,了解已知问题和不兼容性。

如果您使用 Jetpack Compose

  • 如果新的 Kotlin 版本低于 2.0.0
    • 找到兼容的 Compose 编译器插件
    • 升级 build.gradle.kts 中出现 kotlinCompilerExtensionVersion 的任何模块中的 kotlinCompilerExtensionVersion
  • 如果新的 Kotlin 版本是 2.0.0 或更高

如果您使用 Kotlin Symbol Processing (KSP),请参阅 KSP 快速入门了解设置,参阅 KSP 发布版本了解可用版本。请注意,您必须使用与 Kotlin 版本匹配的 KSP 版本。例如,如果您使用的是 Kotlin 2.0.21,您可以使用任何以 2.0.21 开头的 KSP 插件版本,例如 2.0.21-1.0.25。您通常不需要升级 KSP 处理器(例如 Room 编译器,它在您的构建文件中显示为 ksp 依赖项);KSP 插件抽象了大部分编译器 API,且处理器使用的 KSP API 是稳定的。

升级您正在使用的所有其他 Kotlin Compiler Plugins。Kotlin Compiler Plugin API 通常在发布版本之间发生变化,插件必须使用兼容的 API。如果插件列在编译器插件中,您必须使用与 Kotlin 编译器相同的版本。对于任何其他编译插件,请查阅其文档以获取适当的映射。

请注意,未与 Kotlin 编译器本身一起维护的编译器插件通常会出现发布延迟,因为它们需要等待编译器插件 API 稳定。在升级 Kotlin 之前,请检查您使用的所有编译器插件是否都有匹配的升级版本可用。

最后,在某些情况下,Kotlin 语言会发生变化,需要您更新代码。这最常发生在您尝试实验性功能时。如果在升级 Kotlin 编译器后代码无法正常构建,请在Kotlin 版本说明中检查语言变化或运行时库中断情况。

Kotlin Compiler Plugins

如果您需要升级 Kotlin 编译器插件,请升级到正在使用的 Kotlin 的匹配版本。

大多数 Kotlin 编译器插件要么使用与 Kotlin 编译器相同的版本,要么以所需的 Kotlin 编译器版本开头。例如,如果插件版本为 2.0.21-1.0.25,则您必须使用 Kotlin 编译器的 2.0.21 版本。

更改 Kotlin 编译器版本有时需要其他更改

库是构建中最常升级的依赖项。您会在 Android Studio 编辑器中看到可用的升级。

某些库指定了使用该库所需的最低 compileSdkminSdk。如果您未至少使用指定的 compileSdk,则构建会失败。但是,应用的 minSdk 会自动设置为您的库依赖项和构建文件中指定的所有 minSdk 值的最大值。

某些库还指定了使用的最低 Kotlin 版本。更新构建文件中的 Kotlin 版本,使其至少达到指定的版本。

Gradle

有时新版本的 Gradle 会弃用现有 API,并在未来的版本中移除这些 API。如果您开发了 Gradle 插件,请尽快升级您的插件,特别是如果该插件是公共的。

某些 Gradle 升级需要查找您使用的插件的新版本。请注意,这些插件在开发方面可能会滞后,因为它们需要升级以匹配最新的 Gradle 插件 API。

升级 Gradle

  • 阅读您要使用的版本的版本说明
  • 升级 gradle/wrapper/gradle-wrapper.properties 中的 Gradle 版本。
  • 通过运行 ./gradlew wrapper --gradle-version latest 升级 Gradle wrapper jar 和脚本。
  • 升级您的 Gradle 插件。
  • 升级用于运行 Gradle 的 JDK

Gradle 插件

升级后的 Gradle 插件有时会使用新的或更改的 Gradle API,这反过来又需要升级 Gradle,或者可能需要更改其在构建文件中的配置。在这两种情况下,您都会看到构建警告或错误来指示不兼容性。

无论何时升级插件,都要升级 Gradle。

Android SDK

Android Studio 包含一个 Android SDK 升级助手,可以帮助完成这些任务。

如果您使用助手或手动执行升级,请考虑以下事项

Android SDK 的每个版本都包含新功能和 API、错误修复和行为更改。Play 商店要求更新您的 targetSdk,但请考虑在截止日期之前更新 targetSdk,以便有更多时间进行任何必要的更改。

在升级 Android SDK 之前,请仔细阅读版本说明。密切关注行为更改部分,其中包含

  • 您需要在安装时或运行时请求的新权限。
  • 已弃用的 API 及替代项。
  • API 或行为中的破坏性更改。
  • 新的 Kotlin 或 Java API,可能会影响您的代码。

行为更改部分可能很长,但请密切关注,因为它通常包含您需要对应用进行的重大更改。

您必须升级 targetSdk 以满足 Play 商店的要求。升级 compileSdk 是可选的,可以让您访问新的 API。请注意,某些库(如 AndroidX)包含最低 compileSdk 要求。

为了在开发期间利用新的 SDK 功能并确保构建期间的兼容性,请升级 Android Gradle 插件 (AGP) 和 Android Studio。这些工具包含用于新 SDK 的新的和改进的工具。请参阅 适用于 Android API 级别的最低工具版本

升级 Android SDK 时,升级您使用的任何 AndroidX 库。AndroidX 通常使用新的和更新的 API,以在 Android SDK 版本之间提供更好的兼容性和性能。

Android Studio

您通常可以随时升级 Android Studio。您可能会看到提示您升级 AGP升级 Android SDK 的消息。强烈建议进行这些升级,但并非强制要求。

如果您稍后想使用 Android Studio 升级 AGP 或 Android SDK,可以在工具菜单中找到这些选项

Java

如果您的 Android 应用中包含 Java 源代码,您可能希望利用较新的 Java API。

每个 Android SDK 版本都支持一部分 Java API 和语言功能。AGP 使用一种称为去糖的过程为较低的 Android SDK 版本提供兼容性。

Android SDK 版本说明指定了支持的 Java 级别和潜在问题。其中一些问题也可能影响 Kotlin 源代码,因为 Kotlin 可以访问相同的 Java API。即使您没有 Java 源代码,也务必密切关注版本说明的行为更改部分中出现的 JDK API 部分。

JDK 的使用在您的构建脚本的多个位置指定。有关详细信息,请参阅Android 构建中的 Java 版本

升级分析

升级依赖项可能会引入风险,表现为 API 和行为更改、新的使用要求、新的安全问题,甚至许可证更改。例如,您是否需要

  • 为 API 更改修改代码?
  • 添加新的权限检查?
  • 为行为更改创建额外的测试或修改现有测试?

请注意,您升级的依赖项也升级了*其*依赖项的版本。这可能迅速导致大量的更改。

如果您使用有助于自动化依赖项升级的插件或工具,请注意它们不会为您进行任何分析;它们只是升级到最新的库版本。请勿假设在进行这些类型的自动升级后一切都会正常工作

成功升级的关键是升级分析

  1. 确定升级前后依赖项的差异。
  2. 检查每个更改并确定涉及的风险。
  3. 降低风险,或接受或拒绝更改。

确定依赖项差异

升级分析的第一步是确定依赖项如何变化。利用版本控制 (VCS,例如 Git) 和 Dependency Guard 插件快速查看更改。您的目标是创建*之前*和*之后*的快照并进行比较。

设置并创建您的第一个基线

在开始升级之前,请确保您的项目成功构建。

理想情况下,解决尽可能多的警告,或者创建基线来跟踪您已看到的警告。

  • Lint

    • 通过添加以下内容将警告视为错误
    android {
        // ...
        lint {
            warningsAsErrors = true
        }
    }
    

    到每个模块的 build.gradle.kts 中。

    • 或者,如果您有许多现有警告,请创建Android lint 基线。随着时间的推移,请务必清除现有警告(或忽略那些您不关心的警告)
  • Kotlin 编译器:启用 -Werror 将所有警告视为错误。请参阅如何定义选项

  • 其他工具:如果您使用其他支持基线跟踪的静态分析工具,请设置其基线。

警告基线可以更容易地查看在升级依赖项时引入的新警告。您可以使用 Dependency Guard 查看依赖项及其版本的更改。

在您的 gradle/libs.versions.toml 版本目录中,添加

[versions]
dependencyGuard = "0.5.0"

[plugins]
dependency-guard = { id = "com.dropbox.dependency-guard", version.ref = "dependencyGuard" }

并将以下内容添加到您的应用构建文件中

Kotlin

plugins {
    alias(libs.plugins.dependency.guard)
}

dependencyGuard {
    configuration("releaseRuntimeClasspath")
}

Groovy

plugins {
    alias(libs.plugins.dependency.guard)
}

dependencyGuard {
    configuration('releaseRuntimeClasspath')
}

releaseRuntimeClasspath 配置是一个可能的目标,但如果您想使用不同的配置,请在您的构建文件中不列出配置的情况下运行 ./gradlew dependencyGuard 以查看所有可用的配置。

设置完成后,运行 ./gradlew dependencyGuard 以在 app/dependencies/releaseRuntimeClasspath.txt 中生成报告。这是您的基线报告。将其提交到您的版本控制系统 (VCS) 以保存它。

请记住,Dependency Guard 只捕获库依赖项的列表。您的构建文件中还有其他依赖项,例如 Android SDK 和 JDK 版本。在依赖项更改之前提交到 VCS 可以让 VCS diff 也高亮显示这些更改。

升级并与您的基线进行比较

一旦有了基线,就可以升级依赖项和您想要测试的其他构建更改。此时不要升级您的源代码或资源。

运行 ./gradlew lint 查看新的 lint 警告或错误。处理任何重要问题,然后通过运行 ./gradlew lint -Dlint.baselines.continue=true 更新您的警告基线。如果您使用其他工具捕获了警告基线,也要处理新警告并更新它们的基线。

运行 ./gradlew dependencyGuard 更新您的基线报告。然后运行您的 VCS diff 查看非库更改。它很可能包含比您想象的更多的库升级。

分析风险

一旦了解了发生了哪些变化,就可以考虑每个升级库的可能风险。这有助于您集中精力进行测试或对更改进行更深入的调查。为您的项目定义一组要分析的风险,以确保分析的一致性。

一些注意事项

主要版本升级

主要版本号变化了吗?

语义化版本控制中,第一个数字被称为主要版本。例如,如果库的版本从 1.2.3 升级到 2.0.1,则主要版本已更改。这通常表明库开发者在版本之间进行了不兼容的更改,例如移除或更改了部分 API。

当您看到这种情况时,在查看以下任何注意事项时,请格外关注受影响的库。

如果您的代码使用了任何实验性 API(这通常需要您使用注解或构建文件规范进行选择),即使是次要或补丁版本更改(例如从 1.2.3 升级到 1.3.1 或从 1.2.3 升级到 1.2.5),也可能带来额外的风险。

非稳定 API

某些库版本可能包含非稳定 API。这些通常是正在开发中的 API 或依赖于其他非稳定 API 的 API。

虽然通常限于预览版(例如 alpha、dev 或实验性版本),但某些库包含标记为实验性或不稳定的 API。

如果可能,请避免使用此类 API。如果需要使用它们,请务必记录您的使用情况,并关注后续版本中的更改或移除。

动态行为

某些库的行为会因外部因素而异。例如,与服务器通信的库取决于该服务器的变化。

  • 该库是否需要匹配某个特定的服务器版本?
  • 该库能否连接到不同版本的服务器?
  • 是否存在其他外部因素影响库的正常行为?

清单合并

以 Android Archives (AAR) 形式发布的库可以包含合并到您的应用中的资源和清单。这可能会添加新的权限和 Android 组件(例如 Activity 或 Broadcast Receiver),这些组件间接运行。

运行时更新

某些库使用可以在您的应用控制之外进行更新的功能。库可能会使用 Play 服务,而 Play 服务独立于 Android SDK 进行升级。其他库可能会绑定到独立更新的外部应用中的服务(通常使用 AIDL)。

您跳过了多少个版本?

您等待升级库的时间越长,潜在风险就越多。如果您看到版本变化很大,例如从 1.2.3 到 1.34.5,请格外关注此库。

迁移指南

检查该库是否提供了迁移指南。这可以显著减少您的风险分析和缓解计划。

请注意,此类指南的存在是一个很好的指标,表明开发者专注于兼容性并考虑了您的升级缓解措施。

版本说明

查看每个更改的库的版本说明(如果提供)。查找表明破坏性更改或新要求的迹象,例如添加的权限。

README 文件

某些库的 README 文件会注明潜在风险,特别是如果该库未提供版本说明。查找*已知问题*,特别是已知的安全问题。

检查已知漏洞

Play SDK 索引跟踪许多流行 SDK 的漏洞。Play 管理中心会报告您是否使用了列出的带有已知漏洞的 SDK。在 Android Studio 中编辑构建文件时,IDE 会检查 SDK 索引并标记使用了易受攻击的库版本。

美国国家标准与技术研究院 (NIST) 维护着一个大型的国家漏洞数据库 (NVD)Dependency Check Gradle 插件根据 NVD 检查您使用的依赖项。

要使用 Dependency Check,请申请 NVD API 密钥设置 Gradle 插件,并运行 ./gradlew dependencyCheckAnalyze。请注意,这可能需要很长时间才能运行。

版本冲突

版本解析是否符合预期?查找冲突,特别是主要版本差异。有关如何查找冲突的详细信息,请参阅Gradle 依赖项解析。特别是,在 ./gradlew app:dependencies 报告中搜索 ->

如果可能,与依赖项的作者合作解决他们的依赖项冲突。如果公司允许,向库贡献更改(上游),以帮助提高库的兼容性。

检查许可证

库开发者许可这些库供您使用。您必须遵守许可证的条款,否则不能使用该库。一些许可证非常宽松,通常只要求注明库的出处并向最终用户展示其许可证文本。有些被认为是*传染性的*;如果您使用这些库,则必须将相同的许可证应用于您的应用或库。有关检查所有依赖项当前许可证集的详细信息,请参阅验证许可证

维护和
质量风险

对于拥有公共仓库的库

  • 有多少贡献者正在维护该库?
  • 上次升级是什么时候,该库多久更改一次?
  • 问题积压(如果可用)是什么情况?快速浏览一下,了解潜在问题和库的技术债务。
  • 单元测试对库的覆盖程度如何?
  • 代码库中是否存在已知的反模式?
  • 该库的文档是否完善?
  • 代码库中是否有许多 _fixme_ 注释?

开源与闭源

如果库是开源的,无论问题出在您的代码还是库代码中,都比闭源更容易调试问题。

最小化闭源依赖项,并在评估期间施加额外的审查。是否有适合您用例的良好替代方案?闭源库提供哪些服务级别协议?如果您选择使用闭源依赖项,请准备编写额外的测试用例来帮助限制风险。

运行构建

构建您的项目。查找新的错误或警告。如果您可以确定是哪个库导致了这些问题,将其记录为升级该库的风险。

如果您看到任何新的弃用警告,将其添加为产生这些警告的库的特定风险。这些警告可能会在后续版本中被移除。如果您想继续使用该库,请投入时间将使用弃用 API 的代码转换为使用其替代 API,或者记录弃用情况,以便关注这些函数以及它们是否会在稍后被移除。

使用 lint 检测 API 问题

Android lint 可以捕获应用中的许多问题,包括因依赖项或 Android SDK 版本更改导致的问题。例如,如果您升级了 compileSdk 并使用了其新 API,lint 会报告在早期 SDK 版本中不可用的那些 API。

Lint 在 Android Studio 编辑器中运行,会在您进行更改时报告问题。但它通常不会作为您在 Studio 中的构建的一部分运行,也不会在您运行命令行构建时运行,除非您使用了 buildlint 目标。

如果您使用持续集成 (CI),请在 CI 构建期间(或至少在夜间构建期间)运行 gradlew buildgradlew lint 来捕获这些类型的错误。

如果您不使用 CI,请务必至少偶尔运行 gradlew lint

特别注意 lint 错误和警告。某些库随其自己的 lint 检查一起发布,这有助于确保其 API 的正确使用。库的某些新版本包含新的 lint 警告和错误,导致构建时出现新报告。

降低风险

确定升级风险后,决定如何降低风险

  • 接受某些风险。有些风险足够低,可以接受,尤其是在升级时间和资源有限的情况下。
  • 直接拒绝某些风险。有些升级可能风险过高,特别是如果您目前用于缓解风险的时间或资源有限。如果需要进行分流,请专注于解决您遇到的错误或所需新功能所需的升级。
  • 缓解剩余风险
    • 考虑将升级批量处理成更小、独立的更改集。这可以降低整体风险并允许部分回滚。
    • 详细调查更改。
    • 测试您的应用以检查意外更改。在需要时添加新的测试,以增强对升级的信心。
    • 发现可疑情况时,查看源代码(如果可用)。
    • 在您的源代码或构建中进行所需的更改。

记录您的决定。如果升级带来的风险在运行您的应用时成为问题,您的风险分析文档可以减少必要的错误分析。

验证许可证

库开发者许可这些库供您使用。您必须遵守许可证的条款,否则不能使用该库。一些许可证非常宽松,通常只要求注明库的出处并向最终用户展示其许可证文本。有些被认为是*传染性的*;如果您使用这些库,则必须将相同的许可证应用于您的应用或库。

许可证可能会随着任何发布版本而更改。无论何时升级,都应验证您使用的依赖项的许可方式是否与您的应用或库兼容。

如果许可证不兼容(或已更改为不再兼容),您就不能使用该版本的库。您可以

  • 联系库所有者,请求继续使用现有许可证或双重许可,以保留旧许可证。
  • 与您的法务团队合作,确定您是否可以更改许可证以实现兼容。
  • 查找具有兼容许可证的另一个库,并根据需要修改您的应用。
  • Fork 库的最后一个兼容版本(如果该许可证允许衍生作品且更改不是追溯的),并进行您自己的更改。