[Gradle翻译]在Android项目中使用版本目录

244 阅读7分钟

本文由 简悦SimpRead 转码,原文地址 proandroiddev.com

版本目录是Gradle的一个新功能,可以帮助我们发现和维护...... 中的依赖关系。

image.png Helena Lopes的色轮

随着项目开始变得更加复杂和模块化,保持所有依赖关系的同步和更新成为一项艰难而恼人的任务。为了解决这个问题,我们有一些替代方案,最近Gradle推出了一个新的方案,使我们的生活变得更容易。

Gradle在7.0版本中引入了一个叫做版本目录的新功能。它代表了一个跨项目使用的类型安全的依赖关系的列表。它也非常灵活,并利用了Gradle目前所能提供的优势。在这篇文章中,我们将看看为什么要使用,如何在Android项目中应用它,以及在潜入之前必须知道的一些要点。

为什么是版本目录?

版本目录是一个非常灵活的解决方案,它利用了现有的Gradle功能,并允许我们做得比其他的更多。一个例子是创建 "捆绑 "的能力,它允许在我们的Gradle文件中添加一个单一的 implementation() 行和一组库。

版本目录文件是可共享的,所以不仅在它内部,而且在多个项目中拥有一个标准配置就更加容易了。如果我们愿意,这种灵活性还支持第三方插件自动更新版本,以获得最新的版本。

另外,除了复合构建,与buildSrc方案相比,它的表现更好。例如,在 "buildSrc "中,当我们增加一个版本号时,构建就会被清理掉,需要重新构建。而版本目录则不是这样的。关于性能的更多细节,请看看Josef Raska这篇伟大的文章。对于更多的见解,这是一个伟大的Twitter主题,可以关注。

为了在我们的项目中配置版本目录,需要一组简单的步骤。

1.创建libs.visions.toml文件

libs.version.toml文件包含所有的依赖性定义,如versionslibrariesbundlesplugins定义。

[versions]
# Define the dependency versions
kotlin = "1.7.10"
compose = "1.2.1"

[libraries]
# Define the libraries
compose_ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
compose_material = { module = "androidx.compose.material:material", version.ref = "compose" }
compose_tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
compose_icons = { module = "androidx.compose.material:material-icons-extended", version.ref = "compose" }

[bundles]
# Define bundles/groups of libraries
compose = ["compose.ui", "compose.material", "compose.tooling", "compose.icons"]

[plugins]
kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

值得一提的是,所有的依赖别名都被Gradle规范化了。这意味着每一个有-_.的别名都将被更新为使用.来代替。例如,别名compose_uicompose-uicompose.ui将被规范为compose.ui

请记住,也可以在settings.gradle(.kts)中定义版本目录,但是为了清楚起见,我们只展示TOML文件。关于这个和其他定义依赖关系的方法的更多信息,请看官方文档

2.为插件创建一个专门的模块

由于我们使用版本目录的复合构建,我们需要一个专用模块。这个模块不需要一个特定的名字,但要确保它不会与你项目中现有的模块冲突。在我们的例子中,让我们把它称为 "插件"。

这个模块至少需要包含两个文件:build.gradle(.kts)settings.gradle(.kts)。第一个用于常规的Gradle配置,另一个用于将我们的libs.visions.toml文件与版本目录功能连接起来。

plugins {
    `kotlin-dsl`
}

repositories {
    mavenCentral()
    google()
}
dependencyResolutionManagement {
    repositories {
        mavenCentral()
    }
    versionCatalogs {
        create("libs") {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}

3.设置复合构建

现在我们已经设置好了版本目录,我们需要设置复合构建,将这个配置与我们的项目联系起来。为了实现这一点,我们需要打开我们项目的settings.gradle文件并加入几行。

pluginManagement {
    includeBuild("plugins")
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

上面的代码将插件管理设置为不仅在远程存储库中寻找依赖,而且在我们的plugins模块中搜索它们。

如果你的项目使用的Gradle版本低于7.2,你将需要在settings.gradle文件上添加一个新的功能预览激活。

enableFeaturePreview("VERSION_CATALOGS")
  1. 使用版本目录

最后,经过这样的设置,我们就可以在我们的Gradle文件中使用依赖性了。

dependencies {
    // Adds a single dependency
    implementation libs.kotlin
    
    // Add a group of dependencies
    implementation libs.bundles.compose

    // Tests dependencies works in the same way
    testImplementation libs.junit
    androidTestImplementation libs.espresso.core
}

值得注意的是

除了已经提到的关于版本目录的要点外,还有几件事情是值得了解的。

自动更新版本

版本目录的优点之一是能够使用工具来自动更新它们。对于GitHub上的项目,RenovateBot是一个很好的工具,可以集成到你的管道中。这个机器人会读取你的libs.version.toml,并自动创建Pull Request来更新你的依赖关系。

我的管道有一套单元和仪器测试,所以我确信新版本是兼容的,我只需点击合并按钮。如果测试失败,我只需将API的变化更新到PR中,就可以了。

image.png Pull request to update the Coroutines version

然而,如果你的项目不支持该插件,一个很好的选择是版本目录更新插件,直接应用到项目的Gradle文件。这两个插件都可以广泛配置,帮助我们保持项目依赖的新鲜度。

不支持预编译的脚本插件

版本目录的注意点之一是,在预编译的脚本插件中,依赖关系和版本是不可见的。换句话说,在libs.version.toml上声明的依赖关系将在你所有模块的build.gradle上可见,但对于自定义/预编译的模块却不可见。

假设我们的项目有几十个library模块,并且希望所有的模块都有相同的基本依赖关系。我们可以创建一个library_dependencies.gradle,其中包含所有这些模块,然后把它应用到相应的模块中。

plugins {
    id("com.android.library")
}

dependencies {
    // Won't be able to find this library
    implementation(libs.android.appcompat)
}

不幸的是,由于一些限制,这个新的Gradle文件将无法找到适当的依赖关系。解决这个问题的一个方法是创建一个扩展函数来手动暴露依赖关系。

internal val VersionCatalog.logcat: Provider<MinimalExternalModuleDependency>
    get() = getLibrary("logcat")

private fun VersionCatalog.getLibrary(library: String) = findLibrary(library).get()

现在,我们不再试图直接访问版本目录,而是手动访问libs引用,implementation函数将访问我们的扩展函数并正常工作。

plugins {
    id("com.android.library")
}

// Manually get the reference for "libs"
val libs: VersionCatalog = 
    extensions.getByType<VersionCatalogsExtension>().named("libs")

dependencies {
    // Use the extension function to access the dependency
    implementation(libs.android.appcompat)
}

这个解决方案的缺点之一是 类型不安全,因为我们使用字符串来寻找依赖关系,而不是生成的代码。关于这种行为的更多信息,我强烈推荐这个GitHub问题,其中有关于这个主题的广泛讨论。

版本目录是处理我们项目中的依赖性管理的一个伟大的新方法。它使库和版本的维护变得更容易,同时也允许Gradle所提供的所有灵活性。一些Android项目已经开始迁移,我们将开始看到越来越多的代码、例子和对这一神奇功能的改进。

像往常一样,我很愿意分享更多复杂的代码来帮助你的学习。第一个Pull Request是关于我的个人项目Alkaa的,它从buildSrc迁移到版本目录。它还增加了一些JVM公约插件来帮助避免重复,但这是下一篇文章的主题。 🙂

另外,有两个来自了不起的项目的PR,我在做迁移的时候经常使用它们作为例子。The first one is ShoppingApp by Sam Edwards and Voice by Paul Woitaschek. 衷心感谢这两位的惊人贡献! ❤️

本文中使用的代码在GitHub上可以找到,在那里你可以一步一步地看到过程中的每一个部分。

官方Gradle文档也有关于如何改善我们项目的依赖性管理的很好的例子和见解。对于更深入和非正式的内容,Cédric Champeau的这篇文章很不错。最后但并非最不重要的是,谷歌的官方Now In Android也使用了版本目录。


www.deepl.com 翻译