Gradle构建生命周期由三个核心阶段组成:初始化阶段、配置阶段和执行阶段。每个阶段都提供了特定的钩子(hooks),允许开发者在关键节点插入自定义逻辑。理解这些钩子对于编写高效、可靠的构建脚本至关重要。
一、完整生命周期流程图
二、初始化阶段钩子
1. settings.gradle 脚本
作用:定义项目结构和多项目配置
执行时机:生命周期最早执行的脚本
示例:
// settings.gradle
println "初始化阶段开始"
rootProject.name = 'MyApp'
include ':app', ':library'
gradle.projectsLoaded {
println "所有项目已加载"
}
2. settingsEvaluated
作用:settings.gradle执行完毕后触发
执行时机:settings文件解析完成后
示例:
// settings.gradle
gradle.settingsEvaluated { settings ->
println "Settings评估完成: ${settings.rootProject.name}"
// 验证项目结构
if (!settings.rootProject.children.find { it.name == 'app' }) {
throw new GradleException("缺少app模块")
}
}
3. projectsLoaded
作用:所有项目对象创建后触发
执行时机:项目实例化完成后
示例:
// settings.gradle
gradle.projectsLoaded {
println "项目加载完成,根项目: ${gradle.rootProject.name}"
// 设置全局属性
gradle.rootProject.ext {
buildTime = new Date()
}
}
三、配置阶段钩子
1. beforeEvaluate
作用:项目评估前触发
执行时机:在项目build.gradle执行前
示例:
// build.gradle
beforeEvaluate { project ->
println "项目${project.name}即将配置"
// 检查必要插件
if (!project.plugins.hasPlugin('java')) {
println "警告: 未应用Java插件"
}
}
2. afterEvaluate
作用:项目评估后触发(最常用)
执行时机:项目build.gradle执行后,任务图生成前
示例:
// build.gradle
afterEvaluate { project ->
println "项目${project.name}配置完成"
// 安全访问Android扩展
if (project.plugins.hasPlugin('com.android.application')) {
android {
applicationVariants.all { variant ->
println "变体: ${variant.name}"
}
}
}
}
3. projectsEvaluated
作用:所有项目评估完成后触发
执行时机:所有项目配置完成后
示例:
// root build.gradle
gradle.projectsEvaluated {
println "所有项目评估完成"
// 跨项目配置验证
def appProject = findProject(':app')
def libProject = findProject(':library')
if (appProject.android.compileSdkVersion != libProject.android.compileSdkVersion) {
throw new GradleException("模块的compileSdkVersion不一致")
}
}
4. taskGraph.whenReady
作用:任务依赖图生成后触发
执行时机:配置阶段结束前,执行阶段开始前
示例:
gradle.taskGraph.whenReady { graph ->
println "任务图已就绪,包含${graph.allTasks.size()}个任务"
// 条件性启用任务
if (graph.hasTask(':app:assembleRelease')) {
tasks.named('runCodeAnalysis').configure {
enabled = true
}
}
// 添加全局前置检查
graph.allTasks.each { task ->
task.doFirst {
println "开始执行: ${task.path}"
}
}
}
四、执行阶段钩子
1. taskGraph.beforeTask
作用:每个任务执行前触发
执行时机:任务实际执行前
示例:
gradle.taskGraph.beforeTask { task ->
// 记录任务开始时间
task.ext.startTime = System.currentTimeMillis()
println "准备执行: ${task.path}"
// 跳过特定任务
if (task.name.contains('Lint') && project.hasProperty('skipLint')) {
task.enabled = false
}
}
2. doFirst
作用:任务执行时最先运行
执行时机:任务动作开始前
示例:
task build {
doFirst {
println "构建开始: ${new Date()}"
// 验证环境
if (!file('config.properties').exists()) {
throw new GradleException("缺少配置文件")
}
}
}
3. doLast
作用:任务执行时最后运行
执行时机:任务动作结束后
示例:
task build {
doLast {
println "构建成功! 耗时: ${System.currentTimeMillis() - startTime}ms"
// 上传产物
if (project.hasProperty('upload')) {
uploadArtifacts()
}
}
}
4. taskGraph.afterTask
作用:每个任务执行后触发
执行时机:任务完成后
示例:
gradle.taskGraph.afterTask { task, state ->
def duration = System.currentTimeMillis() - task.ext.startTime
def status = state.failure ? "失败" : "成功"
println "任务完成: ${task.path} ($status, ${duration}ms)"
// 收集性能数据
project.ext.taskStats = project.ext.taskStats ?: [:]
project.ext.taskStats[task.path] = duration
}
5. buildFinished
作用:整个构建完成后触发
执行时机:所有任务执行完毕后
示例:
gradle.buildFinished { result ->
def status = result.failure ? "失败" : "成功"
println "构建结束: $status"
// 生成构建报告
if (project.ext.taskStats) {
def report = new File(project.buildDir, "build-performance.txt")
report.text = "任务性能报告:\n"
project.ext.taskStats.each { task, duration ->
report.append "${task}: ${duration}ms\n"
}
}
// 发送通知
if (result.failure) {
sendFailureNotification(result.failure)
}
}
五、Android项目中的实用钩子组合
1. 安全配置Android扩展
afterEvaluate {
android {
compileSdkVersion 34
defaultConfig {
applicationId "com.example.app"
minSdk 24
}
buildTypes {
release {
minifyEnabled true
}
}
}
}
2. 构建性能监控
// 记录开始时间
gradle.projectsLoaded {
rootProject.ext.buildStartTime = System.currentTimeMillis()
}
// 任务级监控
gradle.taskGraph.beforeTask { task ->
task.ext.startTime = System.currentTimeMillis()
}
gradle.taskGraph.afterTask { task, state ->
def duration = System.currentTimeMillis() - task.ext.startTime
// 存储任务耗时...
}
// 构建完成报告
gradle.buildFinished { result ->
def totalTime = System.currentTimeMillis() - rootProject.ext.buildStartTime
println "总构建时间: ${totalTime}ms"
generatePerformanceReport()
}
3. 动态任务配置
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(':app:assembleRelease')) {
// 为Release构建添加额外任务
def signTask = tasks.register('signRelease') {
doLast {
println "签名Release APK..."
}
}
tasks.named('assembleRelease').get().finalizedBy(signTask)
// 添加前置检查
tasks.named('packageRelease').get().doFirst {
validateReleaseConfig()
}
}
}
六、钩子使用最佳实践
1. 推荐执行顺序
settings.gradle- 项目结构定义projectsLoaded- 初始化全局属性beforeEvaluate- 早期项目检查afterEvaluate- 安全访问扩展projectsEvaluated- 跨项目验证taskGraph.whenReady- 最终任务调整taskGraph.beforeTask- 任务级别准备doFirst- 任务特定准备doLast- 任务特定收尾taskGraph.afterTask- 任务级别清理buildFinished- 全局收尾工作
2. 性能优化建议
- 避免在配置钩子中执行耗时操作:特别是
beforeEvaluate和afterEvaluate - 惰性访问:使用
ProviderAPI延迟计算 - 条件注册:只在必要时添加钩子
// 仅在需要时添加构建报告
if (project.hasProperty('generateReport')) {
gradle.buildFinished {
generateBuildReport()
}
}
3. 错误处理模式
gradle.taskGraph.whenReady { graph ->
try {
validateBuildEnvironment()
} catch (Exception e) {
graph.allTasks.each { it.enabled = false }
throw e
}
}
gradle.buildFinished { result ->
if (result.failure) {
logger.error("构建失败: ${result.failure.message}")
sendAlert(result.failure)
}
}
七、常见问题解决方案
1. 钩子执行顺序混乱
问题:多个钩子相互影响
解决:明确依赖关系,使用mustRunAfter
task setup { doLast { println "Setup" } }
task config {
dependsOn setup
doLast { println "Config" }
}
gradle.taskGraph.whenReady {
// 确保在配置完成后执行
mustRunAfter config
}
2. 插件配置冲突
问题:插件覆盖了钩子中的配置
解决:在projectsEvaluated中最后覆盖
gradle.projectsEvaluated {
android {
// 最终覆盖配置
compileSdkVersion 34
}
}
3. 多项目钩子管理
问题:根项目与子项目钩子冲突
解决:使用allprojects统一管理
// root build.gradle
allprojects {
afterEvaluate { project ->
// 所有项目统一配置
}
gradle.taskGraph.whenReady {
// 所有项目统一任务配置
}
}