Android 平台的 SDK 依赖项问题解决方法

对于特定共享包或库,如果其他一些包对这些共享包或库具有依赖项,但依赖于不同且互不兼容的共享包版本,这时候便会发生依赖项问题。Gradle 让 Android 开发者的工作变得很轻松:开发者只需在 build.gradle 中添加一个依赖项,所需的库就会无缝纳入 build 文件中。但是,如果两个依赖项依赖于同一个库的不同版本,会发生什么?

如果两个依赖项属于同一个配置(即应用配置),Gradle 能够自动解决依赖项问题。例如,Android 版 Audience Network SDK 依赖于 "exoplayer" 库。如果您需要添加不同版本的 "exoplayer" 库,Gradle 将选择纳入较高版本的库。但是,如果两个依赖项属于不同配置(即应用配置和测试配置),Gradle 就会抛出错误。下面我们将介绍不同的冲突类型和解决方案。

前提条件

请确保先阅读 Audience Network 入门指南Android 入门指南,然后再继续操作。

依赖项冲突

1:属于同一个配置的依赖项的共享库

2:属于不同配置的依赖项的共享库

依赖项冲突解决方案

1:让 Gradle 为您解决依赖项问题

2:排除依赖项的特定版本

3:在 Gradle 中显式定义发生冲突的库

4:强制解决库问题



依赖项冲突

1:属于同一个配置的依赖项的共享库

请参考以下示例。以下代码中的两个依赖项属于同一个配置,并有一个对 "org.hamcrest:hamcrest-core" 库的内部依赖项。由于两个依赖项内部使用同一库的不同版本,系统会将最高版本的库纳入 build 文件中。Gradle 同步的输出结果将清楚表明,系统在最终 build 文件中自动将 hamcrest 库的版本从 1.1 升级到 1.3

// Depends on version 1.3 of org.hamcrest:hamcrest-core
androidTestImplementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation 'org.mockito:mockito-core:1.10.19'  

2:属于不同配置的依赖项的共享库

如果两个依赖项属于不同配置(即应用配置和测试配置),Gradle 就会抛出错误。请参考以下代码片段。第一个依赖项属于应用配置,而第二个依赖项属于测试配置。因此,项目构建会失败,并出现异常。

当运行插桩测试时,主 APK 和测试 APK 使用相同的类路径。如果主 APK 和测试 APK 使用相同的库(例如 Guava),但库的版本不同,则 Gradle 构建会失败。如果 Gradle 未发现这一点,您的应用在测试时和正常运行时可能会表现出不同的行为(包括在其中一种情况下发生崩溃)。

// Depends on version 1.3 of org.hamcrest:hamcrest-core 
implementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation 'org.mockito:mockito-core:1.10.19' 

依赖项冲突解决方案

1:让 Gradle 为您解决

这是最简单的方法,但限用于属于同一个配置的依赖项。例如,您的模块显式依赖于 ExoPlayer 的某个特定版本。然而,Audience Network SDK 已包含了 ExoPlayer 的另一版本。默认情况下,系统会将最高版本纳入 build 文件中。根据以下代码片段,Gradle 同步的输出结果将清楚地说明,它在最终 build 文件中自动将 ExoPlayer 库的版本从 2.4.2 升级到 2.7.3

implementation 'com.google.android.exoplayer:exoplayer-core:2.7.3'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.7.3'
...
// audience-network-sdk depends on exoplayer-core:r2.4.2 and exoplayer-dash:r2.4.2
implementation 'com.facebook.android:audience-network-sdk:4.28.1' 

2:排除依赖项的特定版本

在大多数情况下,开发者可能想控制要将哪个库的版本纳入最终 build 文件中。您可以配置 build.gradle 来实现库版本控制。例如,如果开发者希望使用低版本 ExoPlayer r2.4.0,而不是 Audience Network SDK 中的 r2.4.2,则您可以在声明 "audience-network-sdk" 依赖项时排除此模块。排除标签也适用于属于不同配置的依赖项。

在实际项目中,有许多依赖项会具有同一库的不同版本。在这种情况下,对每个依赖项,您都需要设置排除标签,以便可以将该库的预期版本纳入 build 文件中。

情景 1:属于同一个配置的依赖项
implementation 'com.google.android.exoplayer:exoplayer-core:r2.4.0'
implementation 'com.google.android.exoplayer:exoplayer-dash:r2.4.0'
...
// audience-network-sdk depends on exoplayer-core:r2.4.2 and exoplayer-dash:r2.4.2
implementation ('com.facebook.android:audience-network-sdk:4.28.1') {
    exclude group: 'com.google.android.exoplayer', module:'exoplayer-core'
    exclude group: 'com.google.android.exoplayer', module:'exoplayer-dash'
}
情景 2:属于不同配置的依赖项
// Depends on version 1.3 of org.hamcrest:hamcrest-core 
implementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation ('org.mockito:mockito-core:1.10.19'){
    exclude group: 'org.hamcrest', module:'hamcrest-core'
}

3:在 Gradle 中显式定义发生冲突的库

当属于不同配置的依赖项发生冲突时,这是较简便的解决方法。在这种情况下,无论依赖项属于哪种配置,我们都需要显式提及要纳入最终 build 文件中的特定库版本。

这种冲突解决方法较为简便,但缺点在于:在更新 junitmockito 等实际依赖项时,开发者也需要更新发生冲突的库。

// Depends on version 1.3 of org.hamcrest:hamcrest-core 
implementation 'junit:junit:4.12' 
// Depends on version 1.1 of org.hamcrest:hamcrest-core
androidTestImplementation 'org.mockito:mockito-core:1.10.19' 
// Explictly mention that include version 1.3 of org.hamcrest:hamcrest-core
androidTestCompile 'org.hamcrest:hamcrest-core:1.3' 

4:强制解决库问题

这是解决冲突的另一种方法。该方法不是为一种配置声明,而是强制为所有配置声明。在以下示例中,您可以将自己的 "resolutionStrategy" 添加到模块级 build.gradle 中,从而强制将指定的包版本纳入 build 文件中,无论依赖项属于同一个配置还是不同配置。

该方法应谨慎使用。在第一个示例中,如果 audience-network-sdk 更新,并且这些库更新了 exoplayer-core 和 exoplayer-dash 库的版本,我们仍会强制使用早期版本。虽然第二种解决方案的方法适用于此种情况,但是在第四种方法中,我们会对所有配置(而不是仅对一个配置)强制使用依赖项的这一版本。

情景 1:属于同一个配置的依赖项
android {
    configurations.all {
        resolutionStrategy.force 'com.google.android.exoplayer:exoplayer-core:r2.4.0'
        resolutionStrategy.force 'com.google.android.exoplayer:exoplayer-dash:r2.4.0'
    }
}
情景 2:属于不同配置的依赖项
android {
    configurations.all {
        resolutionStrategy.force 'org.hamcrest:hamcrest-core:1.1'
    }
}

后续步骤

更多资源

入门指南

Audience Network 入门技术指南

API 参考文档

iOS 版 Facebook SDK 参考文档