0%

Gradle 依赖管理的几种方式

背景

随着项目的发展扩张,项目区分多个 module 的做法越来越常见,同时项目中的依赖也越来越多,不同 module 引入的版本也不一致。有必要对项目中的依赖做梳理,达到统一管理、单独配置,版本号统一的目的。

gradle 依赖管理的一些方案

gradle 随着版本的发展,也不断涌现出一些不同的方案。包括:原始手动管理extra 扩展管理buildSrc 方案composing build 方案以及 gradle 7.0 新出的 version catalog 等。

原始手动管理

手动管理的方式如下所示,自己引入需要的库,该方法最直观简单,但是在每次升级依赖库时需要做大量手动修改调整;同时不同 module 中容易存在重复的配置,且不容易管理版本更新。

1
2
3
4
5
6
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
implementation 'com.squareup.okhttp3:okhttp:4.9.2'
}

google 推荐,使用 extra 扩展

google 在配置项目全局属性 中推荐这种管理方式。

在根目录下build.gradle中,添加ext代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
ext {
appcompatVer = "1.3.1"
materialVer = "1.4.0"
constraintlayoutVer = "2.1.1"
okhttpVer = "4.9.2"

dependencies = [
appcompat : "androidx.appcompat:appcompat:$appcompatVer",
material : "com.google.android.material:material:$materialVer",
constraintlayout: "androidx.constraintlayout:constraintlayout:$constraintlayoutVer",
okhttp : "com.squareup.okhttp3:okhttp:$okhttpVer"
]
}

在 module 目录下 build.gradle 中引入使用

1
2
3
4
5
6
dependencies {
implementation rootProject.ext.dependencies.appcompat
implementation rootProject.ext.dependencies.material
implementation rootProject.ext.dependencies.constraintlayout
implementation rootProject.ext.dependencies.okhttp
}

这种方法比手动管理好了很多,可以做到统一版本号,统一管理;但是他也有缺点,就是在 IDE 中无法关联自动补全,同时 ext 里依赖项太多时,这个文件会变得很长。可以参考 使用循环优化Gradle依赖管理 处理。

buildSrc + Kotlin

上面介绍的方法,不支持代码提示,不支持 IDE 跳转,在多模块开发时相同的依赖需要复制粘贴,使用 buildSrc +Kotlin 可以解决这个问题。

gradle 在运行时会去检查是否存在 buildSrc 的目录,如果有的话,gradle 会自动编译并测试这段代码,并将其放入构建脚本的路径中。对于多项目构建,只能有一个 buildSrc 目录,它位于 gradle 根目录下的一个目录。

根目录下创建 buildSrc 目录
新建文件 build.gradle.kts

文件内容如下,同步 gradle

1
2
3
4
5
6
7
plugins {
`kotlin-dsl`
}
repositories {
// 必不可少
jcenter()
}
创建 /src/main/kotlin/Dependencies.kt

文件内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
object Versions {
const val appcompatVer = "1.3.1"
const val materialVer = "1.4.0"
const val constraintlayoutVer = "2.1.1"
const val okhttpVer = "4.9.2"
}

object DependLibs {
const val appcompat = "androidx.appcompat:appcompat:${Versions.appcompatVer}"
const val material = "com.google.android.material:material:${Versions.materialVer}"
const val constraintlayout = "androidx.constraintlayout:constraintlayout:${Versions.constraintlayoutVer}"
const val okhttp = "com.squareup.okhttp3:okhttp:${Versions.okhttpVer}"
}

在 module 中的 build.gradle 中引用,如下所示。

1
2
3
4
5
6
dependencies {
implementation DependLibs.appcompat
implementation DependLibs.material
implementation DependLibs.constraintlayout
implementation DependLibs.okhttp
}

代码支持跳转,也可以支持自动补全,如下图所示

Composing build

buildSrc 在依赖更新时会重新构建整个项目,是对全局所有 module 的配置,因此构建速度较慢;有没有既满足自动补全、单击跳转又在依赖更新时不重新构建整个项目的方法呢。答案是使用 Composing Build

Composing build又称 复合构建,指的是包含其它构建的构建。有点类似于 gradle 多项目构建,不同于独立项目,它是包含包括settings.gradle等配置文件在内的整个完整项目构建。

包含在复合构建中的构建被称为包含构建,包含的构建不与复合构建或其他包含的构建共享任何配置。每个包含的构建都是独立配置和执行的。

新建一个 module,只保留 src/main 下代码源文件和 build.gradle 文件,其余均删除,如图所示:

其中 VersionPlugin 为继承 Plugin 的实现类,代码空实现即可

1
2
3
4
5
class VersionPlugin : Plugin<Project> {
override fun apply(target: Project) {

}
}

ComposingBuildMethod.kt 为定义依赖版本信息类,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ComposingBuildMethod {
object Versions {
const val appcompatVer = "1.3.1"
const val materialVer = "1.4.0"
const val constraintlayoutVer = "2.1.1"
const val okhttpVer = "4.9.2"
}

object DependLibs {
const val appcompat = "androidx.appcompat:appcompat:${Versions.appcompatVer}"
const val material = "com.google.android.material:material:${Versions.materialVer}"
const val constraintlayout =
"androidx.constraintlayout:constraintlayout:${Versions.constraintlayoutVer}"
const val okhttp = "com.squareup.okhttp3:okhttp:${Versions.okhttpVer}"
}
}

最后,build.gradle 中修改如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
buildscript {
repositories {
jcenter()
}
dependencies {
// 因为使用的 Kotlin 需要需要添加 Kotlin 插件
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
}
}

apply plugin: 'kotlin'
apply plugin: 'java-gradle-plugin'

repositories {
// 需要添加 jcenter 否则会提示找不到 gradlePlugin
jcenter()
}

dependencies {
implementation gradleApi()
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72"

}

compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}

gradlePlugin {
plugins {
version {
// 在 app 模块需要通过 id 引用这个插件
id = 'com.bashellwang.versionplugin'
// 实现这个插件的类的路径
implementationClass = 'com.bashellwang.versionplugin.VersionPlugin'
}
}
}

然后,在 setting.gradle 中使用 includeBuild 引用该 module。

1
includeBuild 'versionplugin'

最终在需要使用的 module,即 app module 下的 build.gradle 中引用该插件并使用即可。

1
2
3
4
5
6
7
8
9
10
11
12
plugins {
id 'com.bashellwang.versionplugin'
}
...
dependencies{
// composing build 引用
implementation ComposingBuildMethod.DependLibs.appcompat
implementation ComposingBuildMethod.DependLibs.material
implementation ComposingBuildMethod.DependLibs.constraintlayout
implementation ComposingBuildMethod.DependLibs.okhttp
}

Version Catalogs

官方在 Gradle7.0 推出了一个新特性,可以使用 Catalog 统一依赖版本,它有以下特点:

  • 对于每一个 catalog,gradle 可以生成类型安全的访问器方便在 IDE 中自动补全(注:实测在 build.gradle 中暂未实现,不确定是用法问题还是功能开发中)
  • catalog 对所有 module 可见,可以方便统一管理版本依赖
  • catalog 可以声明 bundles 依赖,就是经常一起使用的依赖的组合
  • catalog 支持依赖版本号与版本名分割,使用 version refrerences 可以在不同依赖之间共享版本号

因为 catalog 还是一个在孵化中的功能,因此要使用时需要在 setting.gradle 中开启功能预览

1
enableFeaturePreview('VERSION_CATALOGS')

然后新建一个 toml 文件,里面是一些版本依赖相关信息,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[versions]
groovy = "3.0.5"
checkstyle = "8.37"
compilesdk = "30"
targetsdk = "30"

[libraries]
retrofit = "com.squareup.retrofit2:retrofit:2.9.0"
groovy-core = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
groovy-json = { module = "org.codehaus.groovy:groovy-json", version.ref = "groovy" }
groovy-nio = { module = "org.codehaus.groovy:groovy-nio", version.ref = "groovy" }
commons-lang3 = { group = "org.apache.commons", name = "commons-lang3", version = { strictly = "[3.8, 4.0[", prefer="3.9" } }

[bundles]
groovy = ["groovy-core", "groovy-json", "groovy-nio"]

之后在 setting.gradle 中进行声明后即可使用:

1
2
3
4
5
6
7
dependencyResolutionManagement {
versionCatalogs {
cataloglibs {
from(files("libs.versions.toml"))
}
}
}

在 app 下的 build.gradle 文件中使用

1
2
3
4
5
// gradle catalog 引用
implementation cataloglibs.retrofit
implementation cataloglibs.groovy.core
implementation cataloglibs.groovy.json
implementation cataloglibs.groovy.nio

源码地址

GitHub

总结

本文对常见依赖管理的几种方式进行了总结梳理,并初步体验了 gradle 7.0 新出的版本管理方式 catalog,项目可根据实际情况选择使用。

参考