背景
随着项目越来越大,module 越来越多,依赖的库也越来越多,依赖管理也越来越混乱。 我们一般会有以下需求:
- 项目依赖统一管理,在单独文件中配置
- 不同 Module 中的依赖版本号统一
管理 Gradle 依赖
说明:下面所有示例都是基于 Gradle 7.3.0 版本
一、原始粗暴式
在 module 的 build.gradle 中直接引入,就像下面代码一样
Groovy
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk 33
defaultConfig {
applicationId "com.example.gradle"
minSdk 21
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
}
- 优点:方便快捷,依赖的时候复制粘贴一气呵成。
- 缺点:多模块中可能会存在不同版本的依赖,进行版本升级的时候改动非常麻烦。
二、ext 式
Gradle 提供了 ext 关键字可以让我们去定义自身所需要的扩展属性,我们可以通过 ext 关键字对我们工程中的依赖进行全局配置。
- 直接在 project 的 build.gradle 中编写 ext 。
Groovy
ext {
versions = [
compileSdk : 33,
minSdk : 21,
targetSdk : 33,
versionCode : 1,
versionName : "1.0.0",
core_ktx : "1.9.0",
appcompat : "1.6.1",
material : "1.6.1",
constraintlayout: "2.1.4"
]
libraries = [
core_ktx : "androidx.core:core-ktx:${versions.core_ktx}",
appcompat : "androidx.appcompat:appcompat:${versions.appcompat}",
material : "com.google.android.material:material:${versions.material}",
constraintlayout: "androidx.constraintlayout:constraintlayout:${versions.constraintlayout}"
]
}
在 module 的 build.gradle 中的使用如下
Groovy
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk versions.compileSdk
defaultConfig {
applicationId "com.example.gradle"
minSdk versions.minSdk
targetSdk versions.targetSdk
versionCode versions.versionCode
versionName versions.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation libraries.core_ktx
implementation libraries.appcompat
implementation libraries.material
implementation libraries.constraintlayout
}
这样,所有的 module 需要更新依赖库版本的时候,去修改 ext 下的代码即可
- 在另一个 gradle 脚本中进行编写 ext
随着依赖项越来越多,ext 中的代码也越来越庞大,为了更好地管理。我们会把代码写在一个 .gradle ( config.gradle )文件中。
config.gradle 中的代码如下
Groovy
ext {
versions = [
compileSdk : 33,
minSdk : 21,
targetSdk : 33,
versionCode : 1,
versionName : "1.0.0",
core_ktx : "1.9.0",
appcompat : "1.6.1",
material : "1.6.1",
constraintlayout: "2.1.4"
]
libraries = [
core_ktx : "androidx.core:core-ktx:${versions.core_ktx}",
appcompat : "androidx.appcompat:appcompat:${versions.appcompat}",
material : "com.google.android.material:material:${versions.material}",
constraintlayout: "androidx.constraintlayout:constraintlayout:${versions.constraintlayout}"
]
}
在 project 的 build.gradle 中的中引用 config.gradle 文件,方式如下图:
在 module 的 build.gradle 中的使用不变
- 优点:依赖的版本统一管理,版本升级只用修改config.gradle 文件中的 ext 属性。
- 缺点: 在 module 的 build.gradle 中使用时,不支持代码提醒,不支持点击跳转,多 moudle 开发时,不同 module 的依赖需要 ctrl+c/v 导致开发的效率降低
三、kotlin+buildSrc式
ext 可以帮我们很好地解决了管理依赖的问题,美中不足的是它不支持点击跳转、自动补全的问题。这就轮到 buildSrc 大显身手了。
运行 gradle 时,它首先会检查项目中是否存在一个叫 buildSrc 的目录,如果有的话,gradle 会自动编译并测试这段代码,并将其放入构建脚本的类路径中。
- 在 project 的根目录下创建 buildSrc 目录
- 在 buildSrc 中新建 build.gradle 文件
Groovy
apply plugin: 'kotlin'
buildscript {
// 这里保持与主工程版本一致
ext.kotlin_version = '1.7.20'
repositories {
mavenCentral()
google()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
repositories {
mavenCentral()
google()
}
- 在 buildSrc 中创建 /src/main/kotlin目录,然后在新建 com.gradle 包名,最后创建 Dependencies.kt 文件
- Dependencies.kt 中编写依赖管理的代码,如下
Groovy
object Versions {
const val compileSdk = 33
const val minSdk = 21
const val targetSdk = 33
const val versionCode = 1
const val versionName = "1.0.0"
const val core_ktx = "1.9.0"
const val appcompat = "1.6.1"
const val material = "1.6.1"
const val constraintlayout = "2.1.4"
}
object Libraries {
const val core_ktx = "androidx.core:core-ktx:${Versions.core_ktx}"
const val appcompat = "androidx.appcompat:appcompat:${Versions.appcompat}"
const val material = "com.google.android.material:material:${Versions.material}"
const val constraintlayout = "androidx.constraintlayout:constraintlayout:${Versions.constraintlayout}"
}
- 在 module 的 build.gradle 中使用如下
Groovy
import com.gradle.Libraries
import com.gradle.Versions
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk Versions.compileSdk
defaultConfig {
applicationId "com.example.gradle"
minSdk Versions.minSdk
targetSdk Versions.targetSdk
versionCode Versions.versionCode
versionName Versions.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation Libraries.core_ktx
implementation Libraries.appcompat
implementation Libraries.material
implementation Libraries.constraintlayout
}
使用时 Android Stuido 自动提示如下
- 优点:这种方式支持 IDE 输入代码会有提示,会自动完成,并且支持 AndroidStudio 单击跳转。
- 缺点:依赖更新将重新构建整个项目,对编译速度有一定的影响。而且 buildSrc 是编译期才生效,并且其生命周期在project 的 build.gadle 执行之后,所以没办法把插件相关的配置给维护起来。
四、Verison Catalogs 管理
4.1 Version Catalogs 特点
- 对所有 module 可见,可统一管理所有 module 的依赖,Gradle 会为每个依赖目录生成一个类型安全的访问器,同时可以管理 project 的 build.gradle 中的插件依赖。
- 依赖项除了可以声明为单个依赖目录,还可以将多个依赖项声明为依赖目录组。即支持声明依赖bundles, 即总是一起使用的依赖可以组合在一起,
- 支持版本号与依赖名分离,可以在多个依赖间共享版本号
- 支持在单独的 libs.versions.toml 文件中配置依赖
- 支持在项目间共享依赖
4.2 启用 Version Catalogs
在 AGP 低版本中, Version Catalogs 属于孵化中的特效,使用它之前需要启用该特性。 在 settings.gradle 中添加如下代码:
Groovy
pluginManagement {
...
}
enableFeaturePreview("VERSION_CATALOGS")
dependencyResolutionManagement {
...
}
...
4.3 使用 Version Catalogs
在 settings.gradle 中声明,如下:
Groovy
...
dependencyResolutionManagement {
...
// 编写版本目录的依赖库
versionCatalogs {
libs {
// 版本
version('compileSdk', '33')
version('minSdk', '21')
version('targetSdk', '33')
version('versionCode', '1')
version('versionName', '1.0.0')
/**
* 声明依赖
* 例如:别名('coreKtx')、groupId('androidx.core')、artifactId('core-ktx')以及版本('1.9.0')
* alias 在AGP的低版本中使用,高版本中使用 library
*/
// alias('coreKtx').to('androidx.core', 'core-ktx').version('1.9.0')
library('coreKtx', 'androidx.core', 'core-ktx').version('1.9.0')
library('appcompat', 'androidx.appcompat', 'appcompat').version('1.6.1')
library('material', 'com.google.android.material', 'material').version('1.6.1')
library('constraintlayout', 'androidx.constraintlayout', 'constraintlayout').version('2.1.4')
// 除了单个依赖声明,我们也可以将多个依赖项声明为一个依赖组
bundle('appBaseLib', ['coreKtx', 'appcompat', 'material', 'constraintlayout'])
// 针对多个相同版本号的依赖,我们可以定一个通用版本号,即将依赖与版本单独声明并引用
version('lifecycle', '2.2.0')
library('lifecycleExtensions', 'androidx.lifecycle', 'lifecycle-extensions').versionRef('lifecycle')
library('lifecycleRuntimeKtx', 'androidx.lifecycle', 'lifecycle-runtime-ktx').versionRef('lifecycle')
/**
* 声明插件(在高版本的AGP中才有)
*/
plugin('androidApplication', 'com.android.application').version('7.3.0')
plugin('kotlinAndroid', 'org.jetbrains.kotlin.android').version('1.7.20')
}
}
}
...
在 project 的 build.gradle 中的使用如下
Groovy
plugins {
// id 'com.android.application' version '7.3.0' apply false
// id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
/* 依赖插件 等同于上面注释掉的代码 */
alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.kotlinAndroid) apply false
}
在 module 的 build.gradle 中的使用如下
Groovy
plugins {
// id 'com.android.application'
// id 'org.jetbrains.kotlin.android'
/* 依赖插件 等同于上面注释掉的代码 */
alias(libs.plugins.androidApplication)
alias(libs.plugins.kotlinAndroid)
}
android {
compileSdk libs.versions.compileSdk.get().toInteger()
defaultConfig {
applicationId "com.example.gradle"
minSdk libs.versions.minSdk.get().toInteger()
targetSdk libs.versions.targetSdk.get().toInteger()
versionCode libs.versions.versionCode.get().toInteger()
versionName libs.versions.versionName.get()
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
// 依赖单个指定的版本
// implementation libs.coreKtx
// implementation libs.appcompat
// implementation libs.material
// implementation libs.constraintlayout
// 依赖项包
implementation libs.bundles.appBaseLib
implementation libs.lifecycleExtensions
implementation libs.lifecycleRuntimeKtx
}
这种方式支持 IDE 输入代码会有提示,会自动完成,并且支持 AndroidStudio 单击跳转。 但是这种方式会导致 settings.gradle 非常臃肿
通过 toml 文件声明 version catalogs
除了在 settings.gradle 文件中直接声明依赖目录,官方更推荐使用 toml 文件来声明依赖目录,而且在 toml 中编写代码时 Android Studio 会有提示,非常的方便。
首先在项目根目录下创建 libs.versions.toml 文件,文件名可以任意取,并编写如下依赖内容:
Groovy
[versions]
agp = "7.3.0"
kotlin = "1.7.20"
compileSdk = "33"
minSdk = "21"
targetSdk = "33"
versionCode = "1"
versionName = "1.0.0"
coreKtx = "1.9.0"
appcompat = "1.6.1"
material = "1.6.1"
constraintlayout = "2.1.4"
lifecycle = "2.2.0"
[libraries]
coreKtx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
material = { module = "com.google.android.material:material", version.ref = "material" }
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" }
lifecycleExtensions = { module = "androidx.lifecycle:lifecycle-extensions", version.ref = "lifecycle" }
lifecycleRuntimeKtx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" }
[bundles]
appBaseLib = ["coreKtx", "appcompat", "material", "constraintlayout"]
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
toml 文件的使用说明
- toml 文件由 4个主要部分组成
[versions] 用于声明可以被依赖项引用的版本
[libraries] 用于声明依赖的别名
[bundles] 用于声明依赖包(依赖组)
[plugins] 用于声明插件(AGP低版本不支持)
- 声明 libraries 时,可以使用
Groovy
coreKtx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
或者
Groovy
coreKtx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
随后在 setting.gradle 中引用该 toml 文件
Groovy
...
dependencyResolutionManagement {
...
// 编写版本目录的依赖库
versionCatalogs {
libs {
from(files("./libs.versions.toml"))
}
}
}
...
在 module 的 build.gradle 中的使用方式不变。
注意:Version Catalogs 中变量命名大小写敏感,且以小写字母开头, 命名中可以如包含 "-" 或者 "_" 或者 "." ,在相当于分组了。
举例说明:
Groovy
lifecycle_runtime_ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" }
在使用时
Groovy
implementation libs.lifecycle.runtime.ktx
4.4 发布到远端
如果你没有共享依赖的需求,那么上面的方式已经足够使用了。
但是现在基本上都是组件化开发了,特别是当组件数量比较多或者公司内有其他 App 时,大概率就会有统一依赖版本以及共享依赖的需求了。那我们可以把 Version Catalog 发布到远端,其他项目需要时直接依赖然后使用即可,这样就很方便的实现了版本的统一以及快速依赖了。毕竟拷贝来拷贝去非常麻烦且不好维护。
Version Catalog发布也比较简单,按照 官方文档 来即可。
总结
Android 发展至今,各种新技术层出不穷,版本管理也出现了很多方案,这些方案并没有绝对的优劣,还是需要结合实际项目需求来选择的,但是新的方案还是需要学习了解的。