轻松掌握Gradle的依赖管理的规则

前言

    随着软件项目的规模持续扩张,复杂度与日俱增,依赖管理已成为开发进程中不容小觑的关键环节。在 Android 项目里,我们借助 Gradle 来处理依赖之间的关系。通常而言,我们无需过多关注依赖关系。然而,一旦 Gradle 的默认规则难以应对相应的依赖冲突,我们就必须对其规则具备清晰且精准的认知,如此方能妥善处理好依赖之间的关系。

ccdb3912e8a0dcd05544e6232e84ace3.jpeg

这里为了方便讲解,假设有一个项目,里面包含 app模块 和 D 模块;还有三个 Library 依赖,分别是 依赖 A、依赖B、依赖C,我们需要使用 implement 或者 api 来引用它们。

implement 和 api

在最新的 Gradle 版本中,我们使用 implement 或者 api 来声明依赖,而不是使用之前的 compile

  • implement:用于声明模块内部的依赖,这些依赖不会被暴露给模块的依赖者。这意味着当你的模块被其他模块依赖时,使用 implementation 声明的依赖不会传递给依赖你的模块。implementation 具有隐藏依赖的效果,防止依赖的意外传递,同时减少了不必要的依赖检查和处理,提高了编译速度和改善构建性能

  • api:声明的依赖不仅在模块内部可用,还会被传递给依赖当前模块的其他模块。

假设我们使用implement来声明依赖,其关系如下图所示。app 模块引用了 模块D,而 模块D 使用implement 引用了依赖 A、和 依赖 B。由于 implement 会隐藏模块内部的依赖,因此就算app 模块引用了模块 D,也不能使用依赖 A、和 依赖 B内的代码。

4a4f9f7d75a8d47e408905b5742c1c12.jpeg

如果你使用 api 来代替 implement 来声明依赖 B吗,这时我们就在app 模块中使用依赖 B依赖 C中代码了。

ad0bcd1d02dd2165a71dc1ce58d27751.jpeg

需要注意的是,在app 模块处使用 implement 或者 api 来引用模块 D,其效果是一样的。如下图所示:

2602da966dfe5c2b19f38ee9abe6cdef.jpeg

依赖管理规则

在讲解依赖管理规则之前,我们需要先了解一下为什么我们需要进行依赖管理。主要有两个原因:

  1. 依赖有不同的版本,比如 Kotlin 版本有 1.5、1.6、1.7等等

  2. 依赖也会有依赖,比如 retrofit 库就依赖了 okhttp

为了能正常地使用依赖,我们就需要对依赖进行管理。下面就介绍一下,Gradle 是如何管理依赖的。

同一模块下依赖不同的版本

0a9456da19bf10abc3bfbb701c215180.jpeg

如上图所示,app 模块分别引用了 依赖 A依赖 B依赖 C;其中 依赖 A依赖 B 内部也分别引用了不同版本的 依赖 C。这时,Gradle 只会引入版本号最高的依赖,也就是依赖 C 0.0.2。我们可以得出结论,在同一模块下依赖不同的版本,Gradle会选择版本号最高的一个。

代码示例如下:

// 依赖 C 0.0.1 中的代码
Class C {
    fun method() {
      println("0.0.1")
    }
}
// 依赖 C 0.0.2 中的代码
Class C {
    fun method() {
      println("0.0.2")
    }
}
// 依赖 A
fun mehod() {
  val c = C()
  // 由于 Gradle 使用了 0.0.2 版本的依赖库,这里打印结果会变成 0.0.2
  c.method()
}
// 依赖 B
fun mehod() {
  val c = C()
  c.method()
}

不同模块下依赖不同的版本

6b3f22fb6ff74079b7dcb7ae39d7f160.jpeg

如上图所示,如果此时我们再引用模块 D,其中模块 D引用了 依赖 C 0.0.3。此时,Gradle 还是引入了版本号最高的依赖,也就是依赖 C 0.0.3结论2:在不同模块下依赖了不同的版本,Gradle会选择版本号最高的一个。

排除传递依赖

如果你不想要传递依赖影响我们设置的依赖,我们可以使用 exclude 来排除传递依赖。代码示例如下:

dependencies {
    implementation('com.example.b:B:0.0.2') {
        exclude group: 'com.example.c', module: 'C'
    }
}

这里的依赖关系图如下所示。由于排除了 依赖 C 0.0.2,因此 Gradle 会使用 依赖 C 0.0.1

1f22f1c086690745f967db6ffb0b84a1.jpeg

需要注意的是,排除依赖只针对当前的依赖项。如果其他地方也依赖或者传递依赖了 依赖 C 0.0.2Gradle 还是会使用版本号大的 依赖 C 0.0.2。如下图所示:

9bea7ea5eed4db32116398c3bb1e2a67.jpeg

强制依赖

除了排除依赖外,你还可以使用strictly 或者 force 来强制 Gradle 使用指定的依赖。其中force 是旧 Gradle 版本支持的api,建议是使用最新的 strictly。这里讨论的强制依赖的规则,都是使用的 strictly 。使用 strictly 的代码示例如下:

implementation('com.example.c:C') {
    version{
        strictly("0.0.1")
    }
}
// 简化写法是在依赖后面加上 !!
implementation 'com.example.c:C:0.0.1!!'

假设我们在app 模块模块 D中都设置了强制依赖,其中 模块 D 中的强制依赖版本比app 模块模块高。这时的规则与想象中的不同,不是依赖最高的版本,而是以app 模块为准。依赖关系如下图所示:

9f440d3cd58e8c83553ca14a8266cfb0.jpeg

如果 app 模块强制依赖版本比 模块 D 中的高,这时 Gradle 会直接报错。依赖关系如下图所示:

76401ce2b2f1004a4d0d23cac817ff3f.jpeg

总结

根据上面的示例,我们可以总结如下 Gradle 的依赖管理规则:

  1. 默认情况下,Gradle 会使用各个模块中版本号最高的依赖

  2. 我们可以使用 exclude 来排除传递依赖的影响,但它只针对当前的依赖项;或者使用 strictly 来强制依赖的版本

  3. 如果有强制依赖,使用强制依赖的版本。如果  app 模块 和其他模块都有强制依赖,以app 模块为准。

作者:小墙程序员
链接:https://juejin.cn/post/7402204174064910363

关注我获取更多知识或者投稿

565c92b7556fe6f615ed79f7a0973d36.jpeg

5d8a2e87c25578806777558e9558400a.jpeg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值