Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

安卓程序依赖冲突解决心得 #228

Open
soapgu opened this issue Oct 27, 2023 · 0 comments
Open

安卓程序依赖冲突解决心得 #228

soapgu opened this issue Oct 27, 2023 · 0 comments
Labels
problem problem or trouble 安卓 安卓

Comments

@soapgu
Copy link
Owner

soapgu commented Oct 27, 2023

  • 版本冲突起源

依赖分为直接依赖和间接依赖,依赖的依赖就是间接依赖,这个比较好理解
由于开源组件的繁荣,依赖的组件加起来非常庞大,而且引用同一个组件库也很普遍。
当然引用同一个组件不同版本也是非常常见

著名的DLL Hell就是由此而来

  • 版本冲突类型

Version Conflict 版本冲突: 在项目依赖关系图中,某个依赖项存在多个版本;
Implementation conflict 实现冲突: 在项目依赖关系图中,多个依赖项存在相同实现。

图片

  • 依赖版本决议

编译(Complie)声明的版本 <> 运行时(Runtime)版本
图片

一个项目中会产生同一个依赖有多个版本是一个很自然的情况。那么怎么决定运行时版本就很关键。
可以理解为版本的最终仲裁。

  • 举一个例子说明
    implementation "com.squareup.retrofit2:retrofit:2.9.0"
    implementation "com.squareup.okhttp3:okhttp:4.9.3"

实际运行时的依赖版本是这样

图片

虽然retrofit里面声明的okhttp编译版本是3.14.9,但是被强行升了上去和我声明的4.9.3一致。
如果我声明的okhttp “拖后腿”会怎么样

比如这样

    implementation "com.squareup.retrofit2:retrofit:2.9.0"
    implementation "com.squareup.okhttp3:okhttp:3.10.0"
图片

看得出来这次是我声明的相对较低的3.10.0版本被升成了3.14.9
可以简单归纳为版本的策略为最高版本策略
后面我会提到

  • Maven 最短路径策略

Maven 构建系统会采用最短路策略,构建系统会选择从根模块到依赖项的最短路来选择版本。
这样的话,如果是后面一次修改会结果相反,是retrofit内的依赖版本“降级”处理,因为我声明的okhttp:3.10.0更短

  • Gradle 最高版本策略

Gradle 构建系统会采用最高版本策略,构建系统会选择依赖关系图中满足约束规则的最高版本。
感觉这个策略虽然激进一些,但是对于大多数更新活跃的组件来说,升级到最新稳定版本是一个“万能策略”。
对于那些"断更"的组件这个策略问题比较大。

  • 依赖版本分析查看

就一句gradle命令最靠谱

./gradlew app:dependencies --configuration releaseRuntimeClasspath

其他的是使用Android Studio的IDE,可以用但是不推荐

  • Navigate > Class
  • Gradle Tool
图片
  • 依赖冲突实例解析

接下来我们来实战挑战下

  1. 出现依赖冲突

先展示一下一脸懵逼的报错

Duplicate class kotlin.collections.jdk8.CollectionsJDK8Kt found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)
Duplicate class kotlin.internal.jdk7.JDK7PlatformImplementations found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21)
Duplicate class kotlin.internal.jdk7.JDK7PlatformImplementations$ReflectSdkVersion found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21)
Duplicate class kotlin.internal.jdk8.JDK8PlatformImplementations found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)
Duplicate class kotlin.internal.jdk8.JDK8PlatformImplementations$ReflectSdkVersion found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)
Duplicate class kotlin.io.path.ExperimentalPathApi found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21)
Duplicate class kotlin.io.path.PathRelativizer found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21)
Duplicate class kotlin.io.path.PathsKt found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21)
Duplicate class kotlin.io.path.PathsKt__PathReadWriteKt found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21)
Duplicate class kotlin.io.path.PathsKt__PathUtilsKt found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21)
Duplicate class kotlin.jdk7.AutoCloseableKt found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21)
Duplicate class kotlin.jvm.jdk8.JvmRepeatableKt found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)
Duplicate class kotlin.random.jdk8.PlatformThreadLocalRandom found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)
Duplicate class kotlin.streams.jdk8.StreamsKt found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)
Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$1 found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)
Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$2 found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)
Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$3 found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)
Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$4 found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)
Duplicate class kotlin.text.jdk8.RegexExtensionsJDK8Kt found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)
Duplicate class kotlin.time.jdk8.DurationConversionsJDK8Kt found in modules jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)
  1. 分析依赖冲突错误信息

这个错误是在build过程中
我们提取下公共变量

jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22) and jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)

不经意仔细看的话是觉得是Version Conflict(版本冲突)

我们再仔细点,漫不经心是会付出沉重代价的!

jetified-kotlin-stdlib-1.8.22 (org.jetbrains.kotlin:kotlin-stdlib:1.8.22)
jetified-kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21)

jetified-kotlin-stdlib和jetified-kotlin-stdlib-jdk8是两个不同的组件
而由于实现了相同的类而造成了冲突,本质上这是一个Implementation conflict(实现冲突)

  1. 查找冲突项

知道了大概方向是第二步,因为一个项目直接间接的依赖项实在太多啦!我们需要依赖工具链

./gradlew app:dependencies --configuration releaseRuntimeClasspath

执行下面脚本
我们可以分别查找org.jetbrains.kotlin:kotlin-stdlib:1.8.22和org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21这两个冲突项

首先,kotlin-stdlib这个依赖项的源头比较多,基本上各个组件都有使用,而且版本是今年6月的比较新。

图片

反过来查下kotlin-stdlib-jdk8:1.6.21的依赖

一个是从绑定库过来的,8.0.2也蛮新鲜的
图片

另一个是从OKHttp组件过来的
图片

查一下OKHttp的依赖项
图片
用的是okio-jvm 3.0,还很“保守”

  1. 修复冲突项
    原则上我们只要把
    org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21升上去就行了。
    从第三方入手比较好一点

如果把okio-jvm升级到最新3.6的话
图片
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9,版本真的好新

修改gradle文件加上
implementation( "com.squareup.okio:okio:3.6.0" )

编译果然成功啦!

  1. 回顾修复依赖项

让我们再执行下app:dependencies 看看

图片 被升级到1.9了

我们再去OKHttp那里的引用看看
图片
也被升级到1.9啦,那冲突自然就没有啦,逻辑验证闭环。

@soapgu soapgu added 安卓 安卓 problem problem or trouble labels Oct 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
problem problem or trouble 安卓 安卓
Projects
None yet
Development

No branches or pull requests

1 participant