在学习 Now in Android 这个项目的时候,相信很多人都会注意到他使用了 build-logic 来构建整个仓库的配置,相信很多人都想知道他跟以前的方式究竟有何异同,本文就抱着常见的问题来逐步学习,到底为何要使用 build-logic?
build-logic 与 buildSrc 到底有何区别?
说到 build-logic 自然离不开 buildSrc,很多人看来他俩的功能差不多,那么为什么要使用 build logic呢?
构建隔离
- buildSrc:作为项目的一部分被构建,当 buildSrc 变更时,整个项目需要重新构建
- build-logic :通过 Gradle 的 Composite Build 功能(
includeBuild
)作为独立构建被包含,享有更好的构建隔离性
缓存机制
- buildSrc:与主项目共享缓存,导致 buildSrc 的变更会使整个项目缓存失效
- build-logic:拥有独立的缓存,修改不会影响主项目的缓存,提高构建效率
版本控制与依赖管理
- buildSrc:直接继承主项目的依赖和版本
- build-logic:可以有自己独立的版本控制和依赖管理
Now in Android 中的 build-logic 结构
Now in Android 项目使用 build-logic
集中管理所有构建逻辑,其主要结构如下:
css
build-logic/
├── convention/
│ ├── src/main/kotlin/
│ │ ├── AndroidApplicationConventionPlugin.kt
│ │ ├── AndroidLibraryConventionPlugin.kt
│ │ ├── AndroidFeatureConventionPlugin.kt
│ │ └── ...其他插件
│ └── build.gradle.kts
├── settings.gradle.kts
└── gradle.properties
在这里面,希望大家对 convention 这个单词有一些了解,它的含义是:习惯的,约定俗成的。所以在这里面是项目里面大家对于代码风格,仓库等模块的约定配置。是一个公共配置模块,是用来存放共有配置的地方。大家都知道,如果修改项目的 build.gradle 会导致缓存内容的失效,但是如果我公共的配置放置在 convention plugin 里面,我的修改就不会引起项目的re-sync,这样会节省开发等待时间。
主要约定插件分析
Now in Android 项目中的 build-logic
包含多个约定插件(Convention Plugins),每个负责特定类型模块的构建配置。以下是核心插件的功能分析:
1. AndroidApplicationConventionPlugin
该插件负责配置应用模块的基本设置:
kt
class AndroidApplicationConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
apply(plugin = "com.android.application")
apply(plugin = "org.jetbrains.kotlin.android")
apply(plugin = "nowinandroid.android.lint")
extensions.configure<ApplicationExtension> {
configureKotlinAndroid(this)
defaultConfig.targetSdk = 35
// ...其他配置
}
// ...其他扩展配置
}
}
}
internal fun Project.configureKotlinAndroid(
commonExtension: CommonExtension<*, *, *, *, *, *>,
) {
commonExtension.apply {
compileSdk = 35
defaultConfig {
minSdk = 21
}
compileOptions {
// Up to Java 11 APIs are available through desugaring
// https://developer.android.com/studio/write/java11-minimal-support-table
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
isCoreLibraryDesugaringEnabled = true
}
}
configureKotlin<KotlinAndroidProjectExtension>()
dependencies {
"coreLibraryDesugaring"(libs.findLibrary("android.desugarJdkLibs").get())
}
}
这个插件应用了基本的 Android 应用插件,并配置了 Kotlin Android、Lint 检查等通用设置。
2. AndroidLibraryConventionPlugin
该插件配置 Android 库模块:
kotlin
class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
apply(plugin = "com.android.library")
apply(plugin = "org.jetbrains.kotlin.android")
apply(plugin = "nowinandroid.android.lint")
extensions.configure<LibraryExtension> {
configureKotlinAndroid(this)
defaultConfig.targetSdk = 35
// 自动生成资源前缀,确保模块资源命名一致性
resourcePrefix = path.split("""\W""".toRegex())
.drop(1).distinct().joinToString(separator = "_").lowercase() + "_"
// ...其他配置
}
// ...其他扩展配置
}
}
}
该插件不仅配置了 Android 库的基本设置,还自动生成了基于模块路径的资源前缀,确保了项目资源命名的一致性。
3. 功能模块插件
针对特定功能需求,Now in Android 项目还提供了多个专门的插件:
- AndroidFeatureConventionPlugin:为功能模块添加常用依赖(ViewModel、Hilt等)
- AndroidRoomConventionPlugin:配置 Room 数据库
- HiltConventionPlugin:配置 Hilt 依赖注入
- AndroidLibraryComposeConventionPlugin:为库模块添加 Compose 支持
插件注册与使用
插件注册
所有插件在 build-logic/convention/build.gradle.kts
中通过 gradlePlugin
DSL 注册:
kotlin
gradlePlugin {
plugins {
register("androidLibrary") {
id = libs.plugins.nowinandroid.android.library.asProvider().get().pluginId
implementationClass = "AndroidLibraryConventionPlugin"
}
// ...其他插件注册
}
}
插件使用
而在需要 library 支持的模块中,如 core/designsystem
:
kotlin
// core/designsystem/build.gradle.kts
plugins {
alias(libs.plugins.nowinandroid.android.library)
alias(libs.plugins.nowinandroid.android.library.compose)
alias(libs.plugins.nowinandroid.android.library.jacoco)
}
android {
namespace = "com.google.samples.apps.nowinandroid.core.designsystem"
}
通过这种方式,复杂的构建逻辑被封装在插件中,模块的构建脚本保持简洁明了。
约定插件的开发
- 首先是创建常见的需要配置的模块,例如上面的代码
AndroidApplicationConventionPlugin
- 配置依赖的插件,例如
apply(plugin = "com.android.application")
configure
对需要统一配置的参数进行配置- 如果 config 的内容较多可以创建一个拓展函数进行配置,例如上面的的
configureKotlinAndroid
整体而言,convention plugin 给大家对于常见的配置环节有了一个统一地方,但是在开发的时候要按照约定进行命名,这样大家才能更快的熟悉其中的代码。
build-logic 的优势
采用 build-logic
方式组织构建逻辑有以下显著优势:
- 提高构建性能:独立的缓存机制减少了不必要的重新构建
- 增强代码复用:避免在每个模块重复相同的构建配置
- 保证构建一致性:所有模块遵循相同的构建标准和最佳实践
- 简化模块配置:大幅减少各模块 build.gradle 文件的复杂度
- 便于维护与更新:集中管理构建逻辑,全局性变更只需修改一处
结尾
通过 Now in Android 项目的实践可以看出,build-logic
作为现代 Gradle 构建逻辑组织方式,相比传统的 buildSrc
方法提供了更好的构建隔离性、缓存机制和维护性。它特别适合大型多模块项目,能有效提高构建效率、减少配置重复,并确保构建逻辑的一致性和可维护性。
随着 Android 项目复杂度的不断提高,采用 build-logic
这类先进的构建组织方式将成为大型项目的优选方案。如果你正在开发或维护一个多模块的 Android 项目,不妨考虑迁移到这种模式,享受它带来的诸多好处。