30、综合实战:从零构建一个Android项目Gradle体系
终于到了最后一章。说实话,写到这里我挺感慨的。前面29章我们拆开了揉碎了讲Gradle的各种细节,从Groovy语法到Kotlin DSL,从依赖管理到自定义插件。但很多同学学完后问我:“老师,这些东西在实际项目中到底怎么串起来?”
嗯,这一章就是回答这个问题的。我会带着你从零开始,搭建一个完整的Android项目Gradle体系。包含多模块架构、Version Catalog、Convention Plugin、自定义插件,最后再配上CI/CD全流程。说白了,这就是我过去几年在几个中大型项目中沉淀下来的“最佳实践模板”。
本章核心目标:构建一个可维护、可扩展、可复用的Gradle构建体系。让新人加入项目时,不用看半天build.gradle才能开始写代码。
30.1 整体架构设计
先看一张图,这是整个Gradle体系的架构分层。我习惯把构建逻辑分成四层,每一层各司其职。
你看,这个分层很清晰。上层依赖下层,下层为上层提供能力。我在项目中遇到过不少团队,把所有配置都塞进根build.gradle里,结果一个文件上千行,谁都不敢动。用这种分层方式,每个文件职责单一,改起来心里有底。
30.2 第一步:创建Version Catalog
先别急着写代码。我个人习惯,任何新项目的第一步都是创建 gradle/libs.versions.toml。为什么?因为依赖管理是Gradle脚本里最乱的部分,先把这个理清了,后面就顺了。
# gradle/libs.versions.toml
[versions]
agp = "8.2.2"
kotlin = "1.9.22"
coroutines = "1.7.3"
compose-bom = "2024.02.00"
compose-compiler = "1.5.10"
retrofit = "2.9.0"
okhttp = "4.12.0"
hilt = "2.50"
room = "2.6.1"
[libraries]
# AndroidX Core
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
# Compose
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
compose-ui = { group = "androidx.compose.ui", name = "ui" }
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
# Network
retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
okhttp-core = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
# DI
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
我的经验:版本号一定要提取成变量,不要直接写在library声明里。否则升级版本时你得一个一个改,很容易漏掉。我曾经因为漏改一个版本号,导致线上出现诡异的运行时崩溃,排查了两天才找到原因。
30.3 第二步:构建Convention Plugin
接下来是最核心的部分——约定插件。说白了,就是把所有模块通用的构建配置抽出来,放到一个独立的Gradle模块里。这样每个业务模块的build.gradle.kts只需要几行代码。
先创建 build-logic 模块:
// build-logic/settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}
rootProject.name = "build-logic"
include(":convention")
然后写一个Android应用模块的约定插件:
// build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt
package com.example.buildlogic
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.*
class AndroidApplicationConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
// 应用核心插件
pluginManager.apply("com.android.application")
pluginManager.apply("org.jetbrains.kotlin.android")
// 配置Android扩展
extensions.configure<com.android.build.gradle.AppExtension> {
compileSdk = 34
defaultConfig {
minSdk = 26
targetSdk = 34
versionCode = 1
versionName = "1.0.0"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
}
}
}
注意:Convention Plugin需要注册到Gradle的插件管理中,否则无法被其他模块引用。具体做法是在 build-logic/convention/build.gradle.kts 中添加 gradlePlugin 块来声明插件ID。
30.4 第三步:多模块项目结构
有了约定插件,业务模块的配置就变得极其简洁。来看看最终的项目结构:
MyApp/
├── settings.gradle.kts
├── build.gradle.kts
├── gradle/
│ └── libs.versions.toml
├── build-logic/
│ ├── settings.gradle.kts
│ └── convention/
│ ├── build.gradle.kts
│ └── src/main/kotlin/.../*.kt
├── app/
│ └── build.gradle.kts
├── feature/
│ ├── home/
│ │ └── build.gradle.kts
│ └── profile/
│ └── build.gradle.kts
└── library/
├── network/
│ └── build.gradle.kts
└── ui/
└── build.gradle.kts
每个业务模块的build.gradle.kts长这样:
// feature/home/build.gradle.kts
plugins {
id("myapp.android.library")
id("myapp.android.compose")
id("myapp.hilt")
}
android {
namespace = "com.myapp.feature.home"
}
dependencies {
implementation(project(":library:network"))
implementation(project(":library:ui"))
implementation(libs.compose.material3)
}
你想想看,以前写一个模块的build.gradle,少说也要三四十行。现在呢?十行搞定。而且所有模块的编译配置完全一致,不会出现A模块用Java 11、B模块用Java 17这种低级错误。
30.5 第四步:自定义插件——自动生成BuildConfig
接下来我们写一个自定义插件。这个插件的功能是:根据Git分支名自动生成BuildConfig字段,方便我们在代码里区分Debug/Release环境。
// build-logic/convention/src/main/kotlin/GitBranchPlugin.kt
class GitBranchPlugin : Plugin<Project> {
override fun apply(project: Project) {
val branchName = getGitBranch()
val isMainBranch = branchName == "main" || branchName == "master"
project.android().defaultConfig {
buildConfigField("String", "GIT_BRANCH", "\"$branchName\"")
buildConfigField("Boolean", "IS_MAIN_BRANCH", "$isMainBranch")
}
}
private fun getGitBranch(): String {
return try {
Runtime.getRuntime()
.exec(arrayOf("git", "rev-parse", "--abbrev-ref", "HEAD"))
.inputStream.bufferedReader().readText().trim()
} catch (e: Exception) {
"unknown"
}
}
}
避坑指南:我曾经在CI环境里忘记安装Git,导致插件执行时报错。后来我加了一个try-catch,捕获异常时返回"unknown"并打印警告日志。这样即使没有Git环境,构建也不会中断。
30.6 第五步:CI/CD全流程配置
最后一步,把整个构建体系接入CI/CD。我用GitHub Actions举例,但思路同样适用于GitLab CI或Jenkins。
# .github/workflows/build.yml
name: Android CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Cache Gradle
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
- name: Build with Gradle
run: ./gradlew assembleDebug --build-cache
- name: Run Tests
run: ./gradlew testDebugUnitTest
- name: Upload APK
uses: actions/upload-artifact@v4
with:
name: app-debug
path: app/build/outputs/apk/debug/app-debug.apk
| 阶段 | 任务 | 说明 |
|---|---|---|
| 代码检查 | detekt + ktlint | 在build之前运行,确保代码风格统一 |
| 编译构建 | assembleDebug | 使用--build-cache加速增量构建 |
| 单元测试 | testDebugUnitTest | 生成测试报告,失败则中断流程 |
| 产物归档 | upload-artifact | 保存APK和测试报告,供后续使用 |
30.7 写在最后
到这里,一个完整的Android项目Gradle体系就搭建完成了。从Version Catalog管理依赖,到Convention Plugin统一配置,再到自定义插件扩展能力,最后用CI/CD把整个流程自动化。这套体系我在三个不同的项目里实践过,每次都能让团队的构建效率提升30%以上。
说实话,Gradle脚本写得好不好,直接决定了团队后续的开发体验。花一周时间把构建体系搭好,后面一年都能受益。这笔账,怎么算都划算。
如果你在实际搭建中遇到什么问题,欢迎随时交流。毕竟,构建脚本这东西,一个人踩坑不如大家一起分享经验。
公众号:蓝海资料掘金营,微信deep3321