Gradle统一管理依赖

背景

随着项目越来越大,module 越来越多,依赖的库也越来越多,依赖管理也越来越混乱。 我们一般会有以下需求:

  1. 项目依赖统一管理,在单独文件中配置
  2. 不同 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 关键字对我们工程中的依赖进行全局配置。

  1. 直接在 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 下的代码即可

  1. 在另一个 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 会自动编译并测试这段代码,并将其放入构建脚本的类路径中。

  1. 在 project 的根目录下创建 buildSrc 目录
  2. 在 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()
}
  1. 在 buildSrc 中创建 /src/main/kotlin目录,然后在新建 com.gradle 包名,最后创建 Dependencies.kt 文件
  1. 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}"
}
  1. 在 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 文件的使用说明

  1. toml 文件由 4个主要部分组成

[versions] 用于声明可以被依赖项引用的版本

[libraries] 用于声明依赖的别名

[bundles] 用于声明依赖包(依赖组)

[plugins] 用于声明插件(AGP低版本不支持)

  1. 声明 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 发展至今,各种新技术层出不穷,版本管理也出现了很多方案,这些方案并没有绝对的优劣,还是需要结合实际项目需求来选择的,但是新的方案还是需要学习了解的。

相关推荐
️ 邪神2 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】标题栏
android·flutter·ios·鸿蒙·reatnative
努力遇见美好的生活2 小时前
Mysql每日一题(行程与用户,困难※)
android·数据库·mysql
图王大胜4 小时前
Android Framework AMS(17)APP 异常Crash处理流程解读
android·app·异常处理·ams·crash·binderdied·讣告
ch_s_t4 小时前
电子商务网站之首页设计
android
豆 腐7 小时前
MySQL【四】
android·数据库·笔记·mysql
想取一个与众不同的名字好难9 小时前
android studio导入OpenCv并改造成.kts版本
android·ide·android studio
Jewel1059 小时前
Flutter代码混淆
android·flutter·ios
Yawesh_best10 小时前
MySQL(5)【数据类型 —— 字符串类型】
android·mysql·adb
曾经的三心草13 小时前
Mysql之约束与事件
android·数据库·mysql·事件·约束
guoruijun_2012_417 小时前
fastadmin多个表crud连表操作步骤
android·java·开发语言