本文由 简悦 SimpRead 转码, 原文地址 juejin.cn
使用 Version Catalog 管理 Android 依赖
前言
前面写了篇《Android 依赖管理实践与总结》,里面关于 Version Catalog 的部分鸽了,最近拿练手项目升级了 gradle7.X,并使用了 kts,正好把 Version Catalog 也实践了下,这里记录下。
版本依赖
Version Catalog 需要 gradle7.x 版本,如果还没升级的话可以先升级下 -_-||。前面 gradle 版本中都是特性,到了 gradle7.4.1 版本好像就稳定版本了。
如果 gradle 版本低于 7.4.1 需要在 setting.gradle.kts 中添加下面代码开启 Version Catalog:
enableFeaturePreview("VERSION_CATALOGS")
如果版本比这更高就不用管它了。这里顺便说个问题,我看好多例子的 setting.gradle.kts 代码里面还有下面这句:
// 开了会报错: 已在类 org.gradle.accessors.dm.RootProjectAccessor中定义了方法 getVersionPlugin()
// 引用本地模块(新版写法,比如“test-library”): implementation(projects.testLibrary)
//enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
我这发现,加上这句会报异常,类似的 enableFeaturePreview,提一下。
说完版本依赖,说下我下面例子的版本环境:
- Gradle 7.5.1
- AGP 7.4.2
下面就开始介绍使用吧。
配置 Version Catalog
上面依赖没问题了,我们就可以在 setting.gradle.kts 的 dependencyResolutionManagement 中去配置 versionCatalogs 了,例子如下:
dependencyResolutionManagement {
versionCatalogs {
// 可以创建多个版本目录
create("libs") {
// 设置版本: alias + version
version("groovy", "3.0.5")
version("checkstyle", "8.37")
// 设置库: alias + group + articact + version
library("groovy-core", "org.codehaus.groovy", "groovy").versionRef("groovy")
library("groovy-json", "org.codehaus.groovy", "groovy-json").versionRef("groovy")
library("groovy-nio", "org.codehaus.groovy", "groovy-nio").versionRef("groovy")
// 版本号可以设置成范围的
library("commons-lang3", "org.apache.commons", "commons-lang3").version {
strictly("[3.8, 4.0[")
prefer("3.9")
}
// 声明一个依赖组
bundle("groovy", listOf("groovy-core", "groovy-json", "groovy-nio"))
// gradle插件的版本
plugin("versions", "com.github.ben-manes.versions").version("0.45.0")
}
}
}
配置好了就可以在 module 中去使用了,不过不是 kotlin 代码而是 build.gradle,下面是上面例子在 app 的 build.gradle 的使用:
dependencies {
// 引用库
implementation(libs.groovy.core)
implementation(libs.groovy.json)
implementation(libs.groovy.nio)
// 引用一组库
implementation(libs.bundles.groovy)
}
// 插件中的使用
plugins {
`java-library`
checkstyle
alias(libs.plugins.versions)
}
效果如图:
上面这些例子都是从 gradle 的官方文档来的,kts 和 groovy 语法可能还不一样,由于版本原因用法也可能不一样,不明白的话可以看下官方文档:
不过,如果只是看下如何配置的话,就没必要写一篇文章了,下面我们来继续看看 Version Catalog 如何通过文件和插件进行配置。
使用文件配置
Android 官方文档里面推荐我们在项目根目录下面的 gradle 目录里面创建一个 libs.versions.toml 来进行配置 Version Catalog,案例如下:
[versions]
ktx = "1.9.0"
[libraries]
androidx-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "ktx" }
配置好了,我们就可以直接用了,不需要导入,gradle 会自动操作:
dependencies {
implementation(libs.androidx.ktx)
}
官方文档要求目录和文件名都不修改,用起来还是挺简单的,但是我们也是可以自定义的。
下面在根目录下新建一个 libs.toml 文件 (文件名任意),配置同样的东西,我们手动导入下,使用方式还是一样:
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
// 从文件中导入,files别写错了
from(files("$rootDir/libs.toml"))
}
}
}
关于 TOML 文件
TOML 文件里面的写法实际和我们在前面手动配置的类似,它主要由 4 个部分组成
- [versions] 用于声明可以被依赖项引用的版本
- [libraries] 用于声明依赖的别名
- [bundles] 用于声明依赖包(依赖组)
- [plugins] 用于声明插件
里面的节点不能随便定义,只能是 versions、libraries、bundles、plugins、metadata 中的一个,下面给个全一点的例子:
[versions]
# 编译版本
compileSdkVersion = "31"
minSdkVersion = "19"
targetSdkVersion = "30"
versionCode = "1"
versionName = "1.0"
[libraries]
# 基本库
kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin"}
core_ktx = { module = "androidx.core:core-ktx", version.ref = "core_ktx"}
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"}
[bundles]
versionBase = [
# kotlin
"kotlin", "core_ktx", "appcompat", "material", "constraintlayout",
# 协程
"kotlinx_coroutines_core", "kotlinx_coroutines_android",
# Jetpack lifecycle
"lifecycle_extensions", "lifecycle_viewmodel_ktx", "lifecycle_livedata_ktx"
]
[plugins]
编译版本使用要稍微注意下,需要做个转换,我试了直接 poml 里面设置 int 型好像不太行:
android {
compileSdk = libs.versions.compileSdkVersion.get().toInt()
defaultConfig {
applicationId = "com.silencefly96.fundark"
minSdk = libs.versions.minSdkVersion.get().toInt()
targetSdk = libs.versions.targetSdkVersion.get().toInt()
versionCode = libs.versions.versionCode.get().toInt()
versionName = libs.versions.versionName.get()
}
}
在文件里面可以通过 “#” 进行注释,整个文件通过节点分成几部分,可以有空行,但是 “=” 号不可以换行,数组里面带逗号倒是可以换行。
我也是通过我 Dependencies.kt 改过来的,可以配合 ctrl + r 来处理这些繁琐的语法替换。
Version Catalog 插件使用
我看很多文章也有提到插件的使用,但是给的例子却有点让人摸不着头脑,我这前面文章正好写了插件的使用,稍微改了改,把 Version Catalog 插件发布到本地,给其他项目使用,希望对读者有所帮助。
首先我们要明白下整个过程的逻辑,搞这个 Version Catalog 插件是干什么,我理解啊,就是我们把通用的 Version Catalog 配置打包成一个 gradle 插件,然后其他项目再引入这个插件,那样这些个项目的版本就能保持一致,不容易冲突。
也就是说,我们得先有个用来发布 gradle 插件的项目,在这打包好 gradle 插件,然后还要有用来引入这个插件的项目,理解好了这些,我们的目的就很明确了。
打包插件
首先我们就得搞个项目来打包插件,可以新建个项目 (也可以是 module,项目更好理解),然后在它的 app 模块的 build.gradle 配置两个插件:
plugins {
id("version-catalog")
id("maven-publish")
}
其中 maven-publish 是用来发布到 maven 仓库的,version-catalog 是用来把 Version Catalog 配置放到插件中的。
配置好上面两个插件,下面就来配置需要打包的 Version Catalog 配置:
catalog {
versionCatalog {
// 从文件中导入
from(files("./gradle/libs.versions.toml"))
// 直接配置
version("groovy", "3.0.5")
version("checkstyle", "8.37")
library("groovy-core", "org.codehaus.groovy", "groovy").versionRef("groovy")
library("groovy-json", "org.codehaus.groovy", "groovy-json").versionRef("groovy")
}
}
和引入 Version Catalog 类似,不过不要理解成了引入,配置方法可以直接配置,也可以从文件中导入。
将要打包进插件的 Version Catalog 配置弄好后就是发布插件了,同样在 app 模块的 build.gradle 中添加:
// 这两个是我项目的配置,不写会出错,可以适当参考下,我是觉得不用
group = "silencefly96.catalog"
version = "1.0.0"
// 发布的task
publishing{
publications {
// 会新建一个catalog-plugin目录
create<MavenPublication>("catalog-plugin") {
// 配置信息,使用: classpath("groupId:artifactId:version"(不能有空格))
groupId = "silencefly96.catalog"
artifactId = "catalog-plugin"
version = "1.0.0"
from(components["versionCatalog"])
}
}
repositories {
// 本地的 Maven(地址设置,部署到本地,也就是项目的根目录下)
maven { url = uri("../catalog_repo") }
}
}
其实这里就是 maven-publish 插件 (老版本还是叫 maven) 的使用了,kts 和 groovy 语法差异有点大,gradle7.x 的用法和之前也不一样,可以查查资料看下你的版本 maven-publish 插件如何用。
关于插件可以看下我这篇文章: Gradle 自定义插件实践与总结
写好后就可以执行 gradle task 了,在侧边栏 gradle 中找到 publishing 执行,就能发现在项目的根目录生成了一个 catalog_repo 目录,里面放的就是我们的 Version Catalog 插件了。
使用插件
生成好插件了,我们就能在其他项目引入它了,引入的方式和上面差不多,在需要引入项目的 setting.gradle.kts 中配置:
dependencyResolutionManagement {
repositories {
// 引入本地仓库依赖
maven{ url = uri("./catalog_repo") }
// 其他依赖
}
versionCatalogs {
create("libs") {
// 从 maven 仓库获取依赖
from("silencefly96.catalog:catalog-plugin:1.0.0")
// 其他配置
}
}
}
这里需要两步,第一步就是在 repositories 引入本地 maven 仓库,第二个就是从刚刚我们打包好的插件中导入 Version Catalog 配置。
导入后,sync 一下,在需要使用的 module 中我们就可以使用依赖了:
插件优化
之前看别人写的插件,可以简化 module 中 build.gradle 的编写,我这也正好实践了下,配合 Version Catalog 一起使用。
添加依赖
编写优化 gradle 的插件,首先要引入 gradle 相关的 api,在 module 的 build.gradle 中添加:
// 插件的依赖关系
dependencies {
implementation(gradleApi())
implementation("com.android.tools.build:gradle:7.4.2")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0")
}
注意这里引入的 gradle 以及 kotlin 插件是给我们代码用的,所以并不是 classpath 引入给 buildscript 用的,要区分开来。
编写插件
接下来就是编写插件了,这里我用的和 Composing build 一样的方式,可以先看下我之前的文章:
或者直接参考别人的源文章:
是时候弃用 buildSrc , 使用 Composing builds 加快编译速度了
下面就是我写的插件代码了:
package com.silencefly96.plugins
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.CommonExtension
import org.gradle.api.JavaVersion
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.ExtensionAware
import org.gradle.kotlin.dsl.configure
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
class ApplicationOptimizePlugin : Plugin<Project> {
@Suppress("UnstableApiUsage")
override fun apply(target: Project) {
// 配置几个个基础插件
with(target.plugins) {
apply("com.android.application")
apply("kotlin-android")
apply("kotlin-kapt")
apply("kotlin-parcelize")
}
// 配置android闭包
target.extensions.configure<ApplicationExtension> {
// compileSdk需要自己设置
// compileSdk = libs.versions.compileSdkVersion.get().toInt()
defaultConfig {
// 这些也都要自己设置
// applicationId = "com.silencefly96.fundark"
// minSdk = libs.versions.minSdkVersion.get().toInt()
// targetSdk = libs.versions.targetSdkVersion.get().toInt()
// versionCode = libs.versions.versionCode.get().toInt()
// versionName = libs.versions.versionName.get()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = 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"
}
buildFeatures {
viewBinding = true
dataBinding = true
}
}
// 配置dependencies闭包
with(target.dependencies) {}
}
}
// 扩展函数,ApplicationExtension里面没有kotlinOptions,要自己写一个
fun CommonExtension<*, *, *, *>.kotlinOptions(block: KotlinJvmOptions.() -> Unit) {
(this as ExtensionAware).extensions.configure("kotlinOptions", block)
}
差不多就是别人的基础上改了点,这里要注意下,application 和 library 模块不能通用,library 的要做点修改:
class LibraryOptimizePlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target.plugins) {
apply("com.android.library")
...
}
// 配置android闭包
target.extensions.configure<LibraryExtension> {
// ...
}
}
}
这里代码基本一样,就是换了 "com.android.library" 插件和 LibraryExtension,本来我想试试 LibraryExtension 和 ApplicationExtension 基类的 CommonExtension,但是好像不行。
注册插件及使用
上面插件写好后,就要去 build.gralle 里面注册插件了:
gradlePlugin {
plugins.register("applicationOptimizePlugin") {
id = "application-optimize-plugin"
implementationClass = "com.silencefly96.plugins.ApplicationOptimizePlugin"
}
plugins.register("libraryOptimizePlugin") {
id = "library-optimize-plugin"
implementationClass = "com.silencefly96.plugins.LibraryOptimizePlugin"
}
}
根据设置好的 id,我们就能在要使用的 module 里面引入,并删除被优化的内容,比如 app 里面:
plugins {
id("application-optimize-plugin")
}
android {
compileSdk = libs.versions.compileSdkVersion.get().toInt()
defaultConfig {
applicationId = "com.silencefly96.fundark"
minSdk = libs.versions.minSdkVersion.get().toInt()
targetSdk = libs.versions.targetSdkVersion.get().toInt()
versionCode = libs.versions.versionCode.get().toInt()
versionName = libs.versions.versionName.get()
}
}
dependencies {
//测试相关
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
//从基础库继承各个依赖
implementation(project(":module_base"))
}
因为引入了 Version Catalog,所以部分内容不能都优化掉,看需要吧,如果不用 Version Catalog 直接全优化掉也可以。
一些思考
搞到这我发现 Version Catalog 有问题啊,这不就是 buildSrc 一样么,每个要使用的模块都要手动去 implementation、api、kapt,没有使用 Composing build 插件那样一键搞定:
class VersionTestPlugin : Plugin<Project> {
override fun apply(target: Project) {
println("VersionTestPlugin")
// 添加afterEvaluate,不然无法添加依赖
target.afterEvaluate {
with(target.dependencies) {
// 测试相关
add("testImplementation", Libs.junit)
add("androidTestImplementation", Libs.ext_junit)
add("androidTestImplementation", Libs.espresso_core)
}
}
}
}
后面想想好像使用 bundles 也是类似啊,而且使用插件的话,里面一键添加的依赖太多的话容易冗余,太少的话又没必要,想想好像 Version Catalog 这样子还更好一些。
反正 ext、buildSrc、Composing build 以及 Version Catalog 看需要使用吧。
参考文章
在学习的过程中参考了一些文章,还是得感谢各位大佬们的贡献:
迁移到 Gradle 7.x 使用 Version Catalogs 管理依赖
是时候弃用 buildSrc , 使用 Composing builds 加快编译速度了
总结
这里实践了下 Android 里面 Version Catalog 的使用,并且实现了 Version Catalog 打包成本地 maven 仓库插件,并在其他项目中依赖使用,感觉还挺有意思的!
Comments | NOTHING