Gradle 依赖项解析

build 文件会指定您的直接依赖项,但其中每个依赖项都可能需要其他依赖项。这些传递依赖项会迅速扩大整体依赖项图,并且通常会出现版本冲突。

minor(新功能)或 patch(bug 修复)部分发生变化时,该库可能仍然兼容,并且不太可能影响您的应用。

例如,假设您的应用依赖于库 A 和库 B,而这两个库又依赖于库 C 的不同版本。

您的应用依赖于库 A 和库 B,而这两个库又依赖于不同版本的库 C。Gradle 会选择最新版本的库 C。
图 1. 传递性版本冲突。Gradle 会解析为最新版本(默认)。

在这种情况下,Gradle 会默认选择库 C 的最新版本,这可能会导致编译或运行时问题。在此示例中,库 C 会解析为 2.1.1,但请注意,库 A 请求的是库 C 1.0.3。版本号的主要部分已更改,表示存在不兼容的更改,例如已移除的函数或类型。这可能会导致从库 A 进行的调用崩溃。

应用的直接依赖项可以同时是传递依赖项。

您的应用依赖于库 A 和库 C。库 A 依赖于较新版本的库 C。Gradle 会选择最新版本的库 C。
图 2. 另一个传递性版本冲突。在这里,Gradle 会解析为传递依赖项版本,并且您的应用会看到该较新版本。

在这种情况下,较新的传递依赖项可能会替换您在应用中直接请求的版本。

Gradle 会查看图中所有依赖项的所有候选版本,以确定每个依赖项的最新版本。您可以使用基本 Gradle 任务更高级的工具来确定 Gradle 已解析每个依赖项的哪些版本。比较此解决方案中的更改对于了解和降低升级风险至关重要。

例如,您可以通过运行 ./gradlew app:dependencies 来使用 Gradle dependencies 任务,以显示应用模块使用的所有依赖项的树状结构。对使用这些库的应用运行此脚本(如图 2 所示),我们会看到

1: releaseRuntimeClasspath - Runtime classpath of /release.
2: +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0
3: |    +--- ... (omitted for brevity) ...
4: +--- com.sample:library.a:1.2.3
5: |    +--- com.sample:library.c:2.1.1
6: |    |    \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*)
7: |    \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*)
8: +--- com.sample:library.c:1.4.1 -> 2.1.1 (*)

报告的此部分会显示为 releaseRuntimeClasspath 配置解析的一些依赖项。

每当您在依赖项报告中看到 -> 时,都表示请求方(您的应用或其他库)使用了该依赖项的意外版本。在许多情况下,这不会造成任何问题,因为大多数库都是为了实现向后兼容性而编写的。不过,某些库可能会进行不兼容的更改,此报告可帮助您确定应用行为出现新问题的原因。

如需详细了解如何使用 Gradle 的依赖项报告,请参阅查看和调试依赖项

您可以在版本目录或物料清单 (BOM) 中直接指定请求的版本。

直接版本规范解析

您指定的依赖项版本将成为版本解析的候选项。

例如,如需在 app/build.gradle.kts 中请求 androidx.compose.ui:ui 库的 1.7.3 版本作为依赖项,请执行以下操作:

dependencies {
    implementation("androidx.compose.ui:ui:1.7.3")
}

版本 1.7.3 成为候选版本。Gradle 会解析为传递依赖项请求的同一库的 1.7.3 及其他版本中的最新版本。

版本目录解决方案

版本目录定义了用于跟踪应用中所用依赖项版本的变量。如果您使用的是版本目录中的变量,则该变量的指定依赖项会添加到版本解析的候选项中。版本目录中的未使用变量会被忽略。

例如,如需在 gradle/libs.versions.toml 文件中将 androidx.compose.ui:ui 的版本 1.7.3 指定为依赖项,请执行以下操作:

[versions]
ui = "1.7.3"

[libraries]
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" }

这会定义一个名为 libs.androidx.compose.ui 的变量来表示库。除非您使用该变量指定依赖项,否则系统不会将此版本视为候选版本。

如需在 app/build.gradle.kts 中请求该库及其版本,请执行以下操作:

dependencies {
    implementation(libs.androidx.compose.ui)
}

Gradle 的解析方式与直接规范相同。

物料清单 (BOM) 解决方案

BoM 中显示的所有库的版本都成为版本解析的候选项。请注意,只有在指定为直接或间接依赖项时,库才会用作依赖项。BoM 中的其他库会被忽略。

BOM 版本会影响您的直接依赖项以及 BOM 中显示的所有传递依赖项。

例如,在 app/build.gradle.kts 中将 BOM 指定为平台依赖项:

dependencies {
    implementation(platform("androidx.compose:compose-bom:2024.10.00"))
    implementation("androidx.compose.ui:ui")
}

您要用作依赖项的任何库都不需要版本规范;请求的版本来自 BoM。

请注意,您还可以使用版本目录为 BoM 和库创建变量。在版本目录中,对于显示在 BoM 依赖项中的库,请省略版本号。

例如,您的版本目录包含 BoM 及其版本号,但未为您从 BoM 中引用的库指定版本:

[versions]
composeBom = "2024.10.00"

[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }

您的 app/build.gradle.kts 使用版本目录中定义的变量引用 BoM 和库:

dependencies {
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.compose.ui)
}

BoM 中指定的该库的版本将成为 Gradle 解析的候选版本。此外,无论您是否直接将 BoM 中指定的所有其他库版本用作依赖项,这些版本都会成为候选版本。

例如,假设 BOM 为库 A、B 和 C 指定了版本。您的应用想要直接将库 A 和库 D 用作依赖项。库 D 使用库 B 作为依赖项。没有任何代码使用库 C。

BOM 包含库 A、B 和 C 的版本。您的应用使用库 A 和库 D 作为依赖项。库 D 使用库 B 作为依赖项。此应用中未直接或间接使用库 C。
图 3. BOM 场景。

库 A、B 和 D 是应用中的依赖项;库 C 会被忽略。 Gradle 会将 BoM 中指定的 A 和 B 版本用作候选版本,即使您未直接将库 B 指定为依赖项也是如此。

如果库 D 请求的库 B 版本低于 2.0.1,Gradle 会解析为 2.0.1。如果库 D 请求了库 B 的更高版本,Gradle 会解析为该版本。