Gradle构建生命周期主要钩子

205 阅读2分钟

Gradle构建生命周期由三个核心阶段组成:初始化阶段配置阶段执行阶段。每个阶段都提供了特定的钩子(hooks),允许开发者在关键节点插入自定义逻辑。理解这些钩子对于编写高效、可靠的构建脚本至关重要。

一、完整生命周期流程图

deepseek_mermaid_20250628_03a30a.png

二、初始化阶段钩子

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. 推荐执行顺序

  1. settings.gradle - 项目结构定义
  2. projectsLoaded - 初始化全局属性
  3. beforeEvaluate - 早期项目检查
  4. afterEvaluate - 安全访问扩展
  5. projectsEvaluated - 跨项目验证
  6. taskGraph.whenReady - 最终任务调整
  7. taskGraph.beforeTask - 任务级别准备
  8. doFirst - 任务特定准备
  9. doLast - 任务特定收尾
  10. taskGraph.afterTask - 任务级别清理
  11. buildFinished - 全局收尾工作

2. 性能优化建议

  • 避免在配置钩子中执行耗时操作:特别是beforeEvaluateafterEvaluate
  • 惰性访问:使用Provider API延迟计算
  • 条件注册:只在必要时添加钩子
// 仅在需要时添加构建报告
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 {
        // 所有项目统一任务配置
    }
}