调试依赖项解析错误

添加依赖项时,您可能会遇到原始依赖项所需的依赖项以及不同依赖项版本之间冲突的问题。以下是分析您的依赖项图并修复常见问题的步骤。

有关修复涉及自定义构建逻辑的依赖项解析错误的指南,请参阅 自定义依赖项解析策略

查看模块依赖项

某些直接依赖项可能具有自己的依赖项。这些被称为传递依赖项。Gradle 会自动收集并添加这些依赖项,而无需您手动声明每个传递依赖项。用于 Gradle 的 Android 插件提供了一个任务,用于显示 Gradle 为给定模块解析的依赖项列表。

对于每个模块,该报告还根据构建变体、测试源集和类路径对依赖项进行分组。以下是应用程序模块的调试构建变体运行时类路径和其已插入测试源集编译类路径的示例报告。

debugRuntimeClasspath - Dependencies for runtime/packaging
+--- :mylibrary (variant: debug)
+--- com.google.android.material:material:1.0.0@aar
+--- androidx.appcompat:appcompat:1.0.2@aar
+--- androidx.constraintlayout:constraintlayout:1.1.3@aar
+--- androidx.fragment:fragment:1.0.0@aar
+--- androidx.vectordrawable:vectordrawable-animated:1.0.0@aar
+--- androidx.recyclerview:recyclerview:1.0.0@aar
+--- androidx.legacy:legacy-support-core-ui:1.0.0@aar
...

debugAndroidTest
debugAndroidTestCompileClasspath - Dependencies for compilation
+--- androidx.test.ext:junit:1.1.0@aar
+--- androidx.test.espresso:espresso-core:3.1.1@aar
+--- androidx.test:runner:1.1.1@aar
+--- junit:junit:4.12@jar
...

要运行该任务,请执行以下操作

  1. 选择 **查看 > 工具窗口 > Gradle**(或单击工具窗口栏中的 **Gradle** )。
  2. 展开 **AppName > 任务 > android**,然后双击 **androidDependencies**。Gradle 执行完该任务后,**运行**窗口应打开以显示输出。

有关在 Gradle 中管理依赖项的更多信息,请参阅 Gradle 用户指南中的 依赖项管理基础知识

排除传递依赖项

随着应用程序范围的扩大,它可以包含许多依赖项,包括直接依赖项和传递依赖项(您的应用程序导入的库所依赖的库)。要排除不再需要的传递依赖项,可以使用以下所示的 exclude 关键字

Kotlin

dependencies {
    implementation("some-library") {
        exclude(group = "com.example.imgtools", module = "native")
    }
}

Groovy

dependencies {
    implementation('some-library') {
        exclude group: 'com.example.imgtools', module: 'native'
    }
}

从测试配置中排除传递依赖项

如果您需要从测试中排除某些传递依赖项,则上面显示的代码示例可能无法按预期工作。这是因为测试配置(例如,androidTestImplementation)扩展了模块的 implementation 配置。也就是说,它始终在 Gradle 解析配置时包含 implementation 依赖项。

因此,要从测试中排除传递依赖项,您必须在执行时执行此操作,如下所示

Kotlin

android.testVariants.all {
    compileConfiguration.exclude(group = "com.jakewharton.threetenabp", module = "threetenabp")
    runtimeConfiguration.exclude(group = "com.jakewharton.threetenabp", module = "threetenabp")
}

Groovy

android.testVariants.all { variant ->
    variant.getCompileConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
    variant.getRuntimeConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
}

注意:您仍然可以在依赖项块中使用 exclude 关键字,如 排除传递依赖项 部分中的原始代码示例所示,以省略特定于测试配置且未包含在其他配置中的传递依赖项。

修复依赖项解析错误

在将多个依赖项添加到应用程序项目时,这些直接依赖项和传递依赖项之间可能会发生冲突。Android Gradle 插件会尝试优雅地解决这些冲突,但某些冲突可能会导致编译时或运行时错误。

为了帮助您调查哪些依赖项会导致错误,检查您的应用程序的依赖项树 并查找出现多次或版本冲突的依赖项。

如果您无法轻松识别重复依赖项,请尝试使用 Android Studio 的 UI 搜索包含重复类的依赖项,如下所示

  1. 从菜单栏中选择 **导航 > 类**。
  2. 在弹出搜索对话框中,确保选中 **包含非项目项** 旁边的框。
  3. 键入构建错误中出现的类的名称。
  4. 检查包含该类的依赖项的结果。

以下部分介绍了您可能遇到的不同类型的依赖项解析错误以及如何修复它们。

修复重复类错误

如果一个类在运行时类路径上出现多次,您将遇到类似于以下的错误

Program type already present com.example.MyClass

此错误通常发生在以下情况下

  • 二进制依赖项包含一个库,您的应用也将其作为直接依赖项包含。例如,您的应用声明对库 A 和库 B 的直接依赖项,但库 A 已经在其二进制文件中包含了库 B。
    • **要解决此问题**,请删除库 B 作为直接依赖项。
  • 您的应用对同一个库具有本地二进制依赖项和远程二进制依赖项。
    • **要解决此问题**,请删除其中一个二进制依赖项。

修复类路径之间的冲突

当 Gradle 解析编译类路径时,它首先解析运行时类路径,并使用结果来确定应将哪些版本的依赖项添加到编译类路径中。换句话说,运行时类路径决定了在下游类路径上对相同依赖项所需的版本号。

您的应用的运行时类路径也决定了 Gradle 为应用测试 APK 的运行时类路径中匹配的依赖项所需的版本号。类路径的层次结构如图 1 所示。

图 1. 出现在多个类路径上的依赖项的版本号必须根据此层次结构匹配。

当同一个依赖项的不同版本出现在多个类路径中时,可能会发生冲突,例如,当您的应用使用implementation 依赖项配置包含一个版本的依赖项,而库模块使用runtimeOnly 配置包含另一个版本的依赖项时。

在解析运行时和编译时类路径上的依赖项时,Android Gradle 插件 3.3.0 及更高版本尝试自动修复某些下游版本冲突。例如,如果运行时类路径包含库 A 版本 2.0,而编译类路径包含库 A 版本 1.0,则插件会自动将编译类路径上的依赖项更新为库 A 版本 2.0,以避免错误。

但是,如果运行时类路径包含库 A 版本 1.0,而编译类路径包含库 A 版本 2.0,则插件不会将编译类路径上的依赖项降级为库 A 版本 1.0,您仍然会遇到类似于以下的错误

Conflict with dependency 'com.example.library:some-lib:2.0' in project 'my-library'.
Resolved versions for runtime classpath (1.0) and compile classpath (2.0) differ.

要解决此问题,请执行以下操作之一

  • 将所需版本的依赖项作为api 依赖项包含到您的库模块中。也就是说,只有您的库模块声明了依赖项,但应用程序模块也将能够通过传递性访问其 API。
  • 或者,您可以在两个模块中都声明依赖项,但应确保每个模块都使用相同版本的依赖项。考虑配置项目范围的属性以确保每个依赖项的版本在整个项目中保持一致。