一、Gradle 基础认知
1.1 Gradle 是什么?
Gradle 是一个基于 Groovy 或 Kotlin DSL 的自动化构建工具,它结合了 Ant 的灵活性和 Maven 的依赖管理能力,并且引入了"约定优于配置"的理念。在 Android 项目中,Google 提供了 Android Gradle Plugin (AGP),它与 Gradle 协同工作,专门用于构建 Android 应用。
1.2 核心概念
- Project:一个 Gradle 构建对应一个或多个 Project。在 Android 中,根目录是一个 Project,每个模块(如 app、library)也是一个 Project。
- Task:构建的最小工作单元,例如编译 Java 代码、打包资源、生成 APK 等。Gradle 通过 Task 依赖图来执行构建。
- Plugin :插件封装了通用的构建逻辑,如
com.android.application插件提供了构建 App 所需的所有 Task 和配置。 - Build Lifecycle :Gradle 构建分为三个阶段:
- 初始化:解析 settings.gradle,确定哪些 Project 参与构建。
- 配置:执行每个 Project 的 build.gradle,生成 Task 依赖图。
- 执行:根据命令行参数执行指定的 Task。
1.3 Android 项目结构
一个典型的 Android 项目包含以下 Gradle 文件:
settings.gradle:声明项目包含的模块。build.gradle (Project level):全局配置,如仓库地址、插件依赖、全局变量。build.gradle (Module level):每个模块的具体配置,如 android 闭包、依赖项。gradle.properties:JVM 参数、Gradle 属性(如并行编译开关)。gradle-wrapper.properties:指定 Gradle 版本和下载地址。
二、Android Gradle Plugin 核心配置
2.1 基本配置
在模块的 build.gradle 中,android 闭包是我们最常打交道的区域:
groovy
android {
compileSdk 34
buildToolsVersion "34.0.0"
defaultConfig {
applicationId "com.example.myapp"
minSdk 21
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
// 签名配置
signingConfigs {
release {
storeFile file("keystore.jks")
storePassword "123456"
keyAlias "key"
keyPassword "123456"
}
}
// 构建类型
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
minifyEnabled false
debuggable true
}
}
// 产品变种(多渠道)
productFlavors {
free {
dimension "version"
applicationIdSuffix ".free"
versionNameSuffix "-free"
}
pro {
dimension "version"
applicationIdSuffix ".pro"
versionNameSuffix "-pro"
}
}
// 源集配置(可自定义代码和资源目录)
sourceSets {
main {
java.srcDirs = ['src/main/java']
res.srcDirs = ['src/main/res']
}
free {
java.srcDirs = ['src/free/java']
}
}
// 编译选项
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
// Kotlin 选项(如果使用 Kotlin)
kotlinOptions {
jvmTarget = '1.8'
}
}
2.2 Build Variants(构建变种)
构建变种 = Build Type (如 debug/release) × Product Flavor (如 free/pro)。每个变种都有自己的输出 APK 名称、代码和资源源集(src/freeDebug/)。使用变种可以实现同一套代码生成不同版本的应用(如免费版/付费版、不同渠道包)。
2.3 依赖管理
2.3.1 依赖配置类型
implementation:只在当前模块内部使用,不会泄露给其他模块。推荐首选。api:将依赖暴露给其他模块,适用于公共库。compileOnly:仅编译时使用,不打包进 APK(如注解处理器)。runtimeOnly:仅运行时需要,编译时不需要。annotationProcessor/kapt:注解处理器。testImplementation:仅用于单元测试。androidTestImplementation:仅用于 Android 测试。
2.3.2 依赖传递与排除
groovy
dependencies {
implementation('com.example:library:1.0') {
exclude group: 'com.unwanted', module: 'unwanted-module'
transitive = false // 禁止传递依赖
}
}
2.3.3 版本冲突解决
Gradle 默认会选择最高版本,但可能会遇到冲突。可以通过以下方式强制指定版本:
groovy
configurations.all {
resolutionStrategy {
force 'com.squareup.okhttp3:okhttp:4.11.0'
// 或者动态选择策略
failOnVersionConflict()
}
}
三、实际使用案例
案例1:统一版本管理
随着项目模块增多,手动维护版本号容易出错。我们通常采用以下几种方式统一管理:
方式一:使用 ext 全局变量(传统)
在项目根 build.gradle 中定义:
groovy
ext {
compileSdkVersion = 34
minSdkVersion = 21
targetSdkVersion = 34
versionCode = 1
versionName = '1.0'
// 依赖版本
androidxAppcompat = '1.6.1'
retrofit = '2.9.0'
}
然后在模块中引用:
groovy
android {
compileSdk rootProject.ext.compileSdkVersion
// ...
}
dependencies {
implementation "androidx.appcompat:appcompat:$rootProject.ext.androidxAppcompat"
}
方式二:Gradle Version Catalog(推荐,Gradle 7.0+)
创建 gradle/libs.versions.toml 文件:
toml
[versions]
compileSdk = "34"
minSdk = "21"
targetSdk = "34"
appcompat = "1.6.1"
[libraries]
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
在模块中:
kotlin
dependencies {
implementation(libs.androidx.appcompat)
}
并可结合 buildSrc 或复合构建实现更高级的复用。
案例2:多渠道打包(友盟统计)
以友盟渠道为例,通常需要为每个渠道生成不同的 APK,并在 AndroidManifest.xml 中替换渠道号。
配置 productFlavors:
groovy
flavorDimensions "channel"
productFlavors {
google {
dimension "channel"
}
huawei {
dimension "channel"
}
xiaomi {
dimension "channel"
}
}
// 批量配置,避免重复
productFlavors.all { flavor ->
// 为每个 flavor 添加一个 manifest placeholder
flavor.manifestPlaceholders = [UMENG_CHANNEL: name]
}
在 AndroidManifest.xml 中使用占位符:
xml
<meta-data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL}" />
生成不同应用名或图标 :可以在对应 flavor 的源集目录(如 src/google/res/values/strings.xml)中覆盖字符串资源。
案例3:自定义构建配置(如自动生成 BuildConfig 字段)
有时候我们需要在代码中获取一些构建时信息,比如 Git 提交号、编译时间。可以通过在 build.gradle 中动态添加 BuildConfig 字段实现:
groovy
android {
defaultConfig {
buildConfigField "String", "GIT_COMMIT", "\"${getGitCommit()}\""
buildConfigField "long", "BUILD_TIME", "${System.currentTimeMillis()}L"
}
}
def getGitCommit() {
try {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--short', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim()
} catch (Exception e) {
return "unknown"
}
}
然后在 Java/Kotlin 代码中通过 BuildConfig.GIT_COMMIT 访问。
案例4:自定义 Task 实现自动化
假设我们需要在编译前自动生成一个版本信息文件,可以编写自定义 Task:
groovy
task generateVersionFile {
doLast {
def versionFile = new File("$buildDir/generated/version/version.txt")
versionFile.parentFile.mkdirs()
versionFile.text = """
Version Name: ${android.defaultConfig.versionName}
Version Code: ${android.defaultConfig.versionCode}
Build Time: ${new Date()}
""".trim()
}
}
// 让预编译任务依赖此 Task
preBuild.dependsOn generateVersionFile
如果想让生成的资源能被代码访问,可以将其添加到源集:
groovy
android.sourceSets.main.assets.srcDirs += "$buildDir/generated/version"
案例5:优化构建速度
大型项目中构建速度是痛点。我们可以做以下优化:
-
启用 Gradle 构建缓存(本地和远程):
properties# gradle.properties org.gradle.caching=true -
启用并行编译和配置缓存:
propertiesorg.gradle.parallel=true org.gradle.configuration-cache=true -
使用最新版 Gradle 和 AGP:每个新版本都包含性能改进。
-
按需配置 :使用
includeBuild替代project依赖,或在开发时只加载需要的模块。 -
避免动态依赖版本 :如
+会导致每次检查新版本,应指定固定版本。 -
分析构建耗时 :运行
./gradlew build --scan生成构建报告,分析瓶颈。 -
优化自定义 Task:确保自定义 Task 正确配置输入输出,避免不必要的重复执行。
案例6:多模块依赖管理
在大型组件化项目中,模块众多,依赖关系复杂。我们常用以下几种方式简化:
- 定义依赖版本常量:如前所述。
- 使用 BOM(Bill of Materials):如 Firebase BOM,统一管理一组库的版本。
- 自定义 Gradle 插件:封装通用配置,避免在每个模块重复写相同的代码。
四、常见问题与解决方案
4.1 依赖冲突
现象 :Duplicate class 或 DexArchiveMergerException。
排查 :运行 ./gradlew :app:dependencies 查看依赖树,找出冲突。
解决 :使用 exclude 或 force 指定版本。
4.2 构建缓慢
- 检查是否使用了动态版本。
- 检查自定义 Task 是否正确地使用了
@Input、@Output注解。 - 使用
--parallel和--configure-on-demand。
4.3 Manifest 合并错误
现象 :Manifest merger failed。
解决 :在 AndroidManifest 中添加 tools:replace 属性,或在模块的 build.gradle 中设置 android.defaultConfig.manifestPlaceholders。
4.4 Gradle 版本兼容性
AGP 对 Gradle 版本有严格对应关系(见 官方文档)。升级时要同时匹配。
4.5 多模块间资源冲突
现象 :资源 ID 重复。
解决 :为每个模块设置 resourcePrefix "module_"。
五、进阶:编写自定义 Gradle 插件
当项目足够大,通用的配置和 Task 越来越多,可以将其抽取成自定义插件,便于复用和版本管理。
5.1 插件实现方式
- buildSrc :在项目根目录创建
buildSrc模块,自动被编译为插件,但变更后需重新构建整个项目。 - 独立插件项目:发布到 Maven 仓库供多个项目使用。
5.2 简单示例(buildSrc 方式)
- 创建
buildSrc/src/main/java/com/example/MyPlugin.groovy(或 Kotlin):
groovy
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.task('myTask') {
doLast {
println "Hello from MyPlugin in ${project.name}"
}
}
}
}
- 在
buildSrc/build.gradle中配置:
groovy
apply plugin: 'groovy'
dependencies {
implementation gradleApi()
implementation localGroovy()
}
- 在其他模块中应用插件:
groovy
apply plugin: com.example.MyPlugin
5.3 高级用法:扩展
插件通常提供 DSL 扩展,让用户配置。例如:
groovy
class MyPluginExtension {
String message = "default"
}
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
def extension = project.extensions.create('myConfig', MyPluginExtension)
project.task('myTask') {
doLast {
println "Message: ${extension.message}"
}
}
}
}
使用方:
groovy
myConfig {
message = "Hello World"
}
六、总结
Gradle 是 Android 开发中不可或缺的一环,掌握它不仅能让我们高效地管理项目,还能通过自动化解决重复劳动。回顾十年历程,从最初被其陡峭的学习曲线折磨,到如今能够随心所欲地定制构建流程,我深刻体会到:理解 Gradle 的核心概念(Task、Plugin、生命周期)比死记硬背语法更重要。实际项目中,建议从小处着手,逐步引入自动化脚本,不断优化构建速度和依赖管理。