背景
随着项目越来越大,module 越来越多,依赖的库也越来越多,依赖管理也越来越混乱。
我们一般会有以下需求:
1. 项目依赖统一管理,在单独文件中配置
2. 不同 Module 中的依赖版本号统一
管理 Gradle 依赖
说明:下面所有示例都是基于 Gradle 7.3.0 版本
一、原始粗暴式
在 module 的 build.gradle 中直接引入,就像下面代码一样
plugins {id 'com.android.application'id 'org.jetbrains.kotlin.android'
}android {compileSdk 33defaultConfig {applicationId "com.example.gradle"minSdk 21targetSdk 33versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility 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 。
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 中的使用如下
plugins {id 'com.android.application'id 'org.jetbrains.kotlin.android'
}android {compileSdk versions.compileSdkdefaultConfig {applicationId "com.example.gradle"minSdk versions.minSdktargetSdk versions.targetSdkversionCode versions.versionCodeversionName versions.versionNametestInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = '1.8'}
}dependencies {implementation libraries.core_ktximplementation libraries.appcompatimplementation libraries.materialimplementation libraries.constraintlayout
}
这样,所有的 module 需要更新依赖库版本的时候,去修改 ext 下的代码即可
2. 在另一个 gradle 脚本中进行编写 ext
随着依赖项越来越多,ext 中的代码也越来越庞大,为了更好地管理。我们会把代码写在一个 .gradle ( config.gradle )文件中。
config.gradle 中的代码如下
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 文件
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 文件
4. Dependencies.kt 中编写依赖管理的代码,如下
object Versions {const val compileSdk = 33const val minSdk = 21const val targetSdk = 33const val versionCode = 1const 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}"
}
5. 在 module 的 build.gradle 中使用如下
import com.gradle.Libraries
import com.gradle.Versionsplugins {id 'com.android.application'id 'org.jetbrains.kotlin.android'
}android {compileSdk Versions.compileSdkdefaultConfig {applicationId "com.example.gradle"minSdk Versions.minSdktargetSdk Versions.targetSdkversionCode Versions.versionCodeversionName Versions.versionNametestInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = '1.8'}
}dependencies {implementation Libraries.core_ktximplementation Libraries.appcompatimplementation Libraries.materialimplementation 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 中添加如下代码:
pluginManagement {...
}enableFeaturePreview("VERSION_CATALOGS")dependencyResolutionManagement {...
}...
4.3 使用 Version Catalogs
在 settings.gradle 中声明,如下:
...
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 中的使用如下
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 falsealias(libs.plugins.kotlinAndroid) apply false
}
在 module 的 build.gradle 中的使用如下
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 falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = '1.8'}
}dependencies {// 依赖单个指定的版本
// implementation libs.coreKtx
// implementation libs.appcompat
// implementation libs.material
// implementation libs.constraintlayout// 依赖项包implementation libs.bundles.appBaseLibimplementation libs.lifecycleExtensionsimplementation libs.lifecycleRuntimeKtx}
这种方式支持 IDE 输入代码会有提示,会自动完成,并且支持 AndroidStudio 单击跳转。但是这种方式会导致 settings.gradle 非常臃肿
通过 toml 文件声明 version catalogs
除了在 settings.gradle 文件中直接声明依赖目录,官方更推荐使用 toml 文件来声明依赖目录,而且在 toml 中编写代码时 Android Studio 会有提示,非常的方便。
首先在项目根目录下创建 libs.versions.toml 文件,文件名可以任意取,并编写如下依赖内容:
[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低版本不支持)
2. 声明 libraries 时,可以使用
coreKtx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
或者
coreKtx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
随后在 setting.gradle 中引用该 toml 文件
...
dependencyResolutionManagement {...// 编写版本目录的依赖库versionCatalogs {libs {from(files("./libs.versions.toml"))}}
}...
在 module 的 build.gradle 中的使用方式不变。
注意:Version Catalogs 中变量命名大小写敏感,且以小写字母开头, 命名中可以如包含 “-” 或者 “_” 或者 “.” ,在相当于分组了。
举例说明:
lifecycle_runtime_ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" }
在使用时
implementation libs.lifecycle.runtime.ktx
4.4 发布到远端
如果你没有共享依赖的需求,那么上面的方式已经足够使用了。
但是现在基本上都是组件化开发了,特别是当组件数量比较多或者公司内有其他 App 时,大概率就会有统一依赖版本以及共享依赖的需求了。那我们可以把 Version Catalog 发布到远端,其他项目需要时直接依赖然后使用即可,这样就很方便的实现了版本的统一以及快速依赖了。毕竟拷贝来拷贝去非常麻烦且不好维护。
Version Catalog发布也比较简单,按照 官方文档 来即可。
总结
Android 发展至今,各种新技术层出不穷,版本管理也出现了很多方案,这些方案并没有绝对的优劣,还是需要结合实际项目需求来选择的,但是新的方案还是需要学习了解的。