Exploration and Practice of the New Generation Version Dependency Management Vesion Catalog

foreword

Some time ago, I used a new version of Android Studio to create a project and wanted to test something. After the project was created, I found that the dependency management of the entire Gradle has undergone tremendous changes.
Let me first talk about the version of Android Studio I use as follows.
insert image description here
The main changes after creating the project are as follows:

  1. The original .gradle has become .gradle.kts, which means that the kts script will definitely be the main script in the future. Compared with grovvy, kts is more convenient to use, the language is unified, and there are prompts.
  2. AGP version upgraded to 8+
  3. The version dependency becomes the following.
    insert image description here
    Clicking on the dependency discovery will jump to libs.versions.tomlthe file in the gradle directory
    insert image description here

This libs.version.toml is actually a dependency management method provided by the new version of Gradle. For details, please refer to the official version catalog document

Before the version catalog, there are actually several ways to manage Gradle dependencies in Android

  1. Directly depend on the most primitive way, it is very troublesome to maintain the dependent version
  2. Declare dependent information in ext, and then use the variables declared in ext to manage dependencies at the dependent place , which initially solves the problem of unifying dependent versions, but it is still very troublesome to write without prompting
  3. Dependency management is performed through the built-in buildSrc plug-in , and dependency information is maintained through code inside the plug-in. When using dependencies, it can be used by importing packages, which is essentially a static variable method. This method is relatively friendly. There are prompts when writing dependencies, and verification will be performed during compilation. Since each change will lead to full compilation, it will have a certain impact on the compilation speed.
  4. Using Include build , you can maintain dependency information in the code like buildSrc. It is similar to buildSrc when used, but the compilation speed is faster, and there are also functions such as prompts and click jumps.

I have used the previous methods. Before the version catalog came out, I used the Include build method for a long time, but there is another problem that cannot be solved, that is, the plug-in version in the root directory cannot be managed. , as shown in the figure, for example, I want to manage the plug-in version of therouter, because I have declared his version in com.yzq.dependency-manager.
insert image description here
However, whether it is buildSrc or Include build, it only takes effect during the compilation period, and its life cycle is also after the root directory build.geadle is executed, so there is no way to maintain the configuration related to the plug-in.

The Vesion Catalog solves this problem.

Use and release of Vesion Catalog

The use of Version Catalog is very simple, and the official documentation has introduced it in great detail. It is recommended to go directly to the official documentation .
It should be noted that the version of Gradle is required to use the Version Catalog. It is recommended to use it directly on the version above AGP8, and there are no strange restrictions.

Here's a quick look at how to use it

method one

Just declare versionCatalogs and related configuration information directly in settings.gradle.kts

dependencyResolutionManagement {
    
    
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
    
    
        google()
        mavenCentral()
     
    }
    versionCatalogs {
    
    
    
        create("androidLibs") {
    
    
            /*声明版本号*/
            version("minSdk", "21")
            version("compileSdk", "33")
            version("targetSdk", "33")
            version("kotlin", "1.8.22")
            /**
             * 声明依赖库
             *  方式一:别名, 依赖库的具体坐标和版本
             *  方式二:别名, group,artifact,version
             */
            library("androidx-appcompat", "androidx.appcompat:appcompat:1.3.1")//方式一
            library("androidx-core-ktx", "androidx", "core:core-ktx").version("1.6.0")//方式二
            /*把依赖库打包 一起依赖*/
            bundle("androidx", listOf("androidx-appcompat", "androidx-core-ktx"))
            /*声明插件*/
            plugin("android-library", "com.android.library").version("8.0.2")
            plugin("kotlin-android", "org.jetbrains.kotlin.android").versionRef("kotlin")
        }


    }
}

Then use it as follows:

plugins {
    
    
//    id("com.android.library")
//    id("org.jetbrains.kotlin.android")
	/*依赖插件 等同于上面注释掉的代码*/
    alias(androidLibs.plugins.android.library)
    alias(androidLibs.plugins.kotlin.android)
}

android {
    
    
    namespace = "com.yzq.mylibrary2"
    compileSdk = androidLibs.versions.compileSdk.get().toInt()
}

dependencies {
    
    
    /*单独依赖*/
    implementation(androidLibs.androidx.core.ktx)
    implementation(androidLibs.androidx.appcompat)
    /*打包依赖*/
    implementation(androidLibs.bundles.androidx)
}

There is a prompt when adding dependencies, and the version of the plug-in is also managed.

way two

The second method is the officially recommended method, which is to manage dependencies in the toml file. This is the default method when creating a new project. The default file path is also given at the beginning of the article, which is in the gradle folder.
insert image description here

As for the writing method, it is also very simple, so I won’t go into details here.

  • [versions] is the declared version
  • [libraries] is to declare dependent libraries
  • [plugins] is to declare the plug-in
  • [bundles] is packaged dependencies, used to simplify dependencies

Examples are as follows:

[versions]
agp = "8.2.0-alpha10"
kotlin = "1.8.21"
core-ktx = "1.9.0"
junit = "4.13.2"
androidx-test-ext-junit = "1.1.5"
espresso-core = "3.5.1"
appcompat = "1.6.1"
material = "1.9.0"
constraintlayout = "2.1.4"
lifecycle-livedata-ktx = "2.6.1"
lifecycle-viewmodel-ktx = "2.6.1"
navigation-fragment-ktx = "2.6.0"
navigation-ui-ktx = "2.6.0"

[libraries]
core-ktx = {
    
     group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
junit = {
    
     group = "junit", name = "junit", version.ref = "junit" }
androidx-test-ext-junit = {
    
     group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
espresso-core = {
    
     group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
appcompat = {
    
     group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = {
    
     group = "com.google.android.material", name = "material", version.ref = "material" }
constraintlayout = {
    
     group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
lifecycle-livedata-ktx = {
    
     group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle-livedata-ktx" }
lifecycle-viewmodel-ktx = {
    
     group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle-viewmodel-ktx" }
navigation-fragment-ktx = {
    
     group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigation-fragment-ktx" }
navigation-ui-ktx = {
    
     group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigation-ui-ktx" }

[plugins]
androidApplication = {
    
     id = "com.android.application", version.ref = "agp" }
kotlinAndroid = {
    
     id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

[bundles]
androidX = {
    
     modules = ["core-ktx", "appcompat", "material", "constraintlayout", "lifecycle-livedata-ktx", "lifecycle-viewmodel-ktx", "navigation-fragment-ktx", "navigation-ui-ktx"] }

The use is exactly the same as method 1. By default, we don't need to configure anything in settings.gradle.kts. Gradle will automatically load the libs.versions.toml file in the gradle directory, and the default name is also libs.
Of course, if you want to change the file name or path, there is no problem at all, just manually configure the dependencies after the change.

For example

  versionCatalogs {
    
    
        create("libs") {
    
    
            from(files("version-catalog/libs.versions.toml"))
        }
    }

publish to remote

If you don't need to share dependencies, then the above method is enough.
But now it is basically component-based development, especially when the number of components is relatively large or there are other apps in the company, there is a high probability that there will be a need for unified dependency versions and shared dependencies.
Then we can publish the Version Catalog to the remote end, and components or other projects can directly depend on it and use it when needed, so that it is very convenient to realize the unification of versions and fast dependence. After all, copying and copying is very troublesome and difficult to maintain.

The release of Version Catalog is also relatively simple, just follow the official documentation .
After the release is complete, you can directly rely on the remote configuration.
Example:

    versionCatalogs {
    
    
        create("libs") {
    
    
           from("com.xeonyu:version-catalog:0.0.2-SNAPSHOT")
        }
}

At this point, we have implemented the shared version directory.

Cooperate with Include Build to simplify configuration

After solving the problem of shared dependencies, we can also cooperate with the previously used Include Build to simplify the daily dependency configuration.
For example, as a module, its configuration is relatively fixed, nothing more than minSdk, targetSdk, confusing configuration, plug-in configuration, etc., and the configuration of each module is basically the same. At the same time, as an internal component of the company, we hope that the version of the component (such as minSdk, targetSdk, etc.) can be consistent with the App version, then we can combine Include Build with version catalog to achieve.
There are also some three-party dependencies such as room, therouter, etc. Their configurations are fixed and complete. We can centralize their configurations by writing plug-ins. When the project needs to be used, one line of code can fix the dependencies.

Example:

plugins {
    
    
    alias(libs.plugins.yzq.android.application)
    alias(libs.plugins.yzq.android.room)
}

The plug-in part is not very general, and generally needs to be coded and customized according to one's own needs, so I won't be verbose here, mainly to provide some ideas and start a discussion.
For the release of the plug-in, please refer to
the Publishing Gradle Plug-in document, and the rest is to write the plug-in release and apply it.

Summarize

All in all, Version Catalog and Include Build can help developers better manage and configure dependent libraries and plug-ins in projects, improve development efficiency, and maintain version consistency. At the same time, by publishing the Version Catalog and writing Gradle plug-ins, the sharing and centralized management of dependencies can be realized.


If you think this article is helpful to you, please give it a thumbs up. It can help more developers. If there are any mistakes in the article, please correct me. For reprinting, please indicate that you are reposting from Yu Zhiqiang’s blog, thank you !

Guess you like

Origin blog.csdn.net/yuzhiqiang_1993/article/details/131501932