/ 引言 /
在大型的软件项目中,特别是 Android 项目中,往往包含多个模块(modules)。每个模块可能负责不同的功能、组件或层次,而且这些模块之间可能存在相互依赖。因此,合理、统一的管理依赖对于项目的可维护性和构建的一致性非常重要。
/ 统一管理依赖的好处 /
统一管理依赖的好处在于提高项目的可维护性、一致性和开发效率。这种做法通常用于大型软件项目,特别是多模块项目,其设计目的主要包括以下方面:
-
版本一致性:这是因为在多模块项目中,如果每个模块都单独管理依赖,就有可能出现不同模块使用不同版本的库,导致潜在的兼容性问题。通过统一管理依赖,可以集中管理版本信息,减少这类问题的发生。
-
可维护性:统一将依赖版本信息和声明集中在一个地方,当需要更新或切换依赖版本时,只需修改一个地方即可,而不必在每个模块的构建文件中进行手动更改。这提高了项目的可维护性,减少了重复的劳动。
-
简化构建版本:通过在单一的地方定义依赖版本,可以减小每个模块的构建脚本的复杂性。模块的构建文件变得更加清晰、简洁,使得代码更容易理解。这对于新加入的开发人员更容易上手,并有助于快速了解项目结构和依赖关系。
/ buildSrc 目录是什么? /
buildSrc 是一个特殊的目录结构,用于在 Gradle 构建中管理共享的构建逻辑和代码。它允许你将构建逻辑和代码从项目的构建脚本中提取出来,以便更好地组织、重用和测试构建逻辑。在 Android 项目中,buildSrc 目录通常用于管理依赖关系和插件版本等信息。
下面将使用 Kotlin+buildSrc 并划分为两步骤演示如何集中管理依赖以及在各个模块中的应用。
01
整合依赖
1. 在根目录新建 buildSrc 目录
2. 在 buildSrc 中新建 build.gradle.kts 文件
在 gradle 文件中引入以下插件块并添加 kotlin-dsl 插件。当您运行 gradle 时,它会检查是否存在名为 buildSrc 然后,Gradle 会自动编译和测试此代码,并将其放入构建脚本的类路径中。您无需提供任何进一步的说明。
// buildSrc/build.gradle.kts
plugins{
`kotlin-dsl`
}
3. 添加资源目录
现在 buildSrc 被正确识别为特殊的模块,还需要在此处创建新的资源目录,用于存放我们的 kt 类文件等等。由于我们使用的是 kotlin 版本的 Gradle,所以看起来与普通 kotlin 一致。
4. 集中管理依赖版本和依赖声明
在 buildSrc 模块中创建 Versions 和 Depenencies 两个文件,主要负责管理依赖版本和依赖声明。从代码中可以看出非常清晰,一目了然。
// buildSrc/src/main/kotlin/Versions
object Versions {
const val compose = "1.4.3"
const val composeMaterial3 = "1.1.1"
const val composeCompiler = "1.4.6"
const val hilt = "2.45"
const val okHttp = "5.0.0-alpha.2"
const val retrofit = "2.9.0"
const val room = "2.5.0"
}
// buildSrc/src/main/kotlin/Dependencies
object Dependencies {
const val composeMaterial = "androidx.compose.material3:material3:${Versions.composeMaterial3}"
const val composeUi = "androidx.compose.ui:ui:${Versions.compose}"
const val composeUiGraphics = "androidx.compose.ui:ui-graphics:${Versions.compose}"
const val composeUiTooling = "androidx.compose.ui:ui-tooling:${Versions.compose}"
const val composeUiToolingPreview = "androidx.compose.ui:ui-tooling-preview:${Versions.compose}"
const val composeRuntime = "androidx.compose.runtime:runtime:${Versions.compose}"
const val hiltAndroid = "com.google.dagger:hilt-android:${Versions.hilt}"
const val hiltCompiler = "com.google.dagger:hilt-android-compiler:${Versions.hilt}"
const val hiltAgp = "com.google.dagger:hilt-android-gradle-plugin:${Versions.hilt}"
const val okHttp = "com.squareup.okhttp3:okhttp:${Versions.okHttp}"
const val okHttpLoggingInterceptor = "com.squareup.okhttp3:logging-interceptor:${Versions.okHttp}"
const val retrofit = "com.squareup.retrofit2:retrofit:${Versions.retrofit}"
const val moshiConverter = "com.squareup.retrofit2:converter-moshi:${Versions.retrofit}"
const val roomRuntime = "androidx.room:room-runtime:${Versions.room}"
const val roomCompiler = "androidx.room:room-compiler:${Versions.room}"
const val roomKtx = "androidx.room:room-ktx:${Versions.room}"
}
02
添加依赖
在 buildSrc 中添加好常用的依赖项后,剩余的工作需要在对应的模块中应用即可。例如模块A负责数据、模块B负责UI,那么在模块A中添加 Room 依赖。
如下代码:相比于原始方法确实方便了很多,只需要引入简单的依赖名称。省去管理版本的操作。但是还不够,因为总是为某一个库捆绑多个依赖项,例如 Room、Retrofit 等等,所以需要更好的办法可以一次性添加。接着往下看
// module-a/build.gradle.kts
dependencies {
...
implementation(Dependencies.roomKtx)
implementation(Dependencies.roomRuntime)
kapt(Dependencies.roomCompiler)
}
回到 buildSrc 的 Gradle 文件中,添加 Kotlin 插件和 Android 工具。
// buildSrc/build.gradle.kts
...
repositories {
google()
mavenCentral()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10")
implementation("com.android.tools.build:gradle:8.1.1")
}
注意:需要把项目级别的 build.gradle.kts 文件的插件引入删掉,因为在同步过程中会报重复引入插件的错误。
所以删除自带的代码,替换为如下代码,只需要添加 hilt 插件即可。
// DependencyManagement/build.gradle.kts
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath(Dependencies.hiltAgp)
}
}
同步一下,在 buildSrc 中创建扩展文件,把常用的几种依赖类型添加进去,方便后期直接使用。
// buildSrc/src/main/kotlin/DependencyHandleExt.kt
fun DependencyHandler.implementation(dependency: String) {
add("implementation", dependency)
}
fun DependencyHandler.test(dependency: String) {
add("test", dependency)
}
fun DependencyHandler.androidTest(dependency: String) {
add("androidTest", dependency)
}
fun DependencyHandler.debugImplementation(dependency: String) {
add("debugImplementation", dependency)
}
fun DependencyHandler.kapt(dependency: String) {
add("kapt", dependency)
}
到 Denpendencies 文件中,通过扩展函数的方式一次性导入库的所有依赖,例如 Room 中的三条依赖。
// buildSrc/src/main/kotlin/Dependencies.kt
object Dependencies {
...
}
// 通过扩展函数的方式一次性导入库的所有依赖
fun DependencyHandler.room(){
implementation(Dependencies.roomKtx)
implementation(Dependencies.roomRuntime)
kapt(Dependencies.roomCompiler)
}
fun DependencyHandler.retrofit(){
implementation(Dependencies.retrofit)
implementation(Dependencies.moshiConverter)
implementation(Dependencies.okHttp)
implementation(Dependencies.okHttpLoggingInterceptor)
}
fun DependencyHandler.compose(){
implementation(Dependencies.composeUi)
implementation(Dependencies.composeMaterial)
implementation(Dependencies.composeRuntime)
implementation(Dependencies.composeUiTooling)
implementation(Dependencies.composeUiGraphics)
implementation(Dependencies.composeUiToolingPreview)
}
fun DependencyHandler.hilt(){
implementation(Dependencies.hiltAndroid)
kapt(Dependencies.hiltCompiler)
}
到此为止,每当我们添加依赖时,只需要在对应的模块中像调用函数一样的引入即可,是不是感觉非常的简洁。如下图:
「 module-a模块:依赖引入 」
「 module-b模块:依赖引入 」
最后一步在 app 模块中添加另外两个模块,并运行测试一下没有问题。如果有疑问可以后台私信留言或者 GitHub 提交 Issues。
源码地址:
https://github.com/AAnthonyyyy/DependencyManagementMultiModule
/ 总结 /
总的来说,这种 Kotlin+buildSrc 依赖管理策略提高了项目的整体可维护性、一致性,简化了构建脚本,使得团队更容易协作,同时也带来了更高的灵活性和适应性。这种设计在大型多模块项目中特别有益,能够有效应对复杂性和变化。