添加依赖项时,您可能会遇到原始依赖项所需的依赖项问题以及不同依赖项版本之间的冲突。本文将介绍如何分析您的依赖项图并修复常见的此类问题。
如需了解如何修复涉及自定义构建逻辑的依赖项解析错误,请参阅自定义依赖项解析策略。
查看模块依赖项
某些直接依赖项可能拥有自己的依赖项。这些被称为传递依赖项。Gradle 会自动收集和添加传递依赖项,而无需您手动声明每个传递依赖项。Android Gradle 插件提供了一项任务,可显示 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
...
要运行该任务,请按以下步骤操作
- 选择 View > Tool Windows > Gradle(或点击工具窗口栏中的 Gradle
)。
- 展开 AppName > Tasks > android,然后双击 androidDependencies。Gradle 执行完任务后,“Run”窗口应打开以显示输出。
有关在 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 来搜索包含重复类的依赖项,如下所示
- 从菜单栏中选择 Navigate > Class。
- 在弹出的搜索对话框中,确保 Include non-project items 旁边的复选框已选中。
- 键入构建错误中出现的类名。
- 检查结果中包含该类的依赖项。
以下部分描述了您可能遇到的不同类型的依赖项解析错误以及如何修复它们。
修复重复类错误
如果一个类在运行时类路径中出现多次,您将收到类似以下内容的错误
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。 - 或者,您可以在两个模块中声明依赖项,但您应确保每个模块使用相同版本的依赖项。考虑配置项目范围属性,以确保每个依赖项的版本在整个项目中保持一致。