11、生命周期与Hook:Gradle构建生命周期(初始化、配置、执行)、各阶段Hook方法

说实话,Gradle 的构建生命周期,是很多初学者容易忽略但又极其重要的知识点。我记得刚接触 Gradle 时,总觉得它就是个“黑盒”——点一下运行,项目就构建好了。直到有一次,我在一个大型多模块项目里遇到了诡异的配置顺序问题,才真正意识到:不理解生命周期,你根本没法掌控 Gradle。

说白了,Gradle 的构建过程就像一场精心编排的舞台剧。它有三个明确的幕次:初始化(Initialization)配置(Configuration)执行(Execution)。每一幕都有它特定的任务,而 Hook 方法就是你在每一幕中插入自己“剧本”的时机。

一、Gradle 构建的三个阶段

我们先来直观地看一下这三个阶段的关系。下面这张图是我自己画的,希望能帮你建立整体认知。

Gradle 构建生命周期三阶段 阶段一:初始化 确定参与构建的项目 解析 settings.gradle 创建 Project 实例 阶段二:配置 执行各项目的 build.gradle 生成 Task 依赖图 确定执行顺序 阶段三:执行 按依赖顺序 执行 Task 生成输出产物 💡 关键理解: • 三个阶段是串行的,前一个阶段完成后才进入下一个阶段 • 配置阶段结束后,Task 的执行顺序就已经完全确定了 • Hook 方法允许你在每个阶段的“边界”插入自定义逻辑

二、第一阶段:初始化(Initialization)

这个阶段 Gradle 会做两件事:第一,确定哪些项目参与构建;第二,为每个项目创建对应的 Project 对象。

它靠什么来知道有哪些项目?就是 settings.gradle 文件。Gradle 会先找到这个文件,然后解析里面的 include 语句。比如你写了 include ':app', ':library',那 Gradle 就会创建两个 Project 实例。

核心要点:初始化阶段只解析 settings.gradle,不会执行任何 build.gradle 文件。

三、第二阶段:配置(Configuration)

这是最容易被误解的阶段。很多人以为 Gradle 一运行就开始执行任务,其实不是。配置阶段的核心工作是:执行所有项目的 build.gradle 脚本,构建出完整的 Task 依赖图

换句话说,这个阶段 Gradle 在“画地图”——它要知道有哪些任务、任务之间的依赖关系、执行顺序是什么。但注意,它只是画地图,还没有真正开始走。

我曾经踩过的坑:在配置阶段里写了耗时的逻辑,比如网络请求或文件读写。结果每次运行 Gradle 命令都特别慢,哪怕只是执行一个简单的 clean 任务。后来才明白,配置阶段的代码每次都会执行,不管你要运行哪个 Task。

四、第三阶段:执行(Execution)

到了这一步,Gradle 才真正开始干活。它会根据配置阶段生成的 Task 依赖图,按顺序执行那些被选中的 Task。比如你运行 gradle build,Gradle 就会执行 compileJava、processResources、test、assemble 等一系列任务。

执行阶段有一个重要特性:只有被命令行参数或依赖关系链上的 Task 才会被执行。没有被引用的 Task 会被跳过,不会浪费任何时间。

五、各阶段的 Hook 方法

现在问题来了:我们怎么在这些阶段的“边界”上插入自己的逻辑?Gradle 提供了丰富的 Hook 方法。我按阶段给你梳理一下。

5.1 初始化阶段的 Hook

这个阶段的 Hook 主要写在 settings.gradle 文件中。

Hook 方法 触发时机 使用场景
settingsEvaluated settings.gradle 解析完成后 检查项目列表、动态添加项目
projectsLoaded 所有 Project 实例创建完成后 对所有项目进行统一配置

举个例子,我在一个微服务项目中用过 settingsEvaluated 来动态排除某些模块:

// settings.gradle
rootProject.name = 'my-project'
include ':service-a', ':service-b', ':service-c'

gradle.settingsEvaluated { settings ->
    // 如果当前环境是 CI,只构建 service-a
    if (System.getenv('CI_ENV') == 'true') {
        settings.include(':service-a')
        // 注意:这里不能直接 remove,需要用 include 重新指定
    }
    println "项目列表已确定:${settings.rootProject.name}"
}

5.2 配置阶段的 Hook

配置阶段的 Hook 最常用,主要写在 build.gradle 文件中。

Hook 方法 触发时机 使用场景
project.beforeEvaluate 当前项目的 build.gradle 执行前 设置默认值、注入公共配置
project.afterEvaluate 当前项目的 build.gradle 执行后 读取已配置的属性、动态添加 Task
gradle.projectsEvaluated 所有项目的 build.gradle 执行完成后 跨项目配置、全局检查
gradle.taskGraph.whenReady Task 依赖图构建完成后 检查 Task 图、修改 Task 属性

我个人最常用的是 afterEvaluate。为什么?因为在这个回调里,所有插件和依赖都已经配置好了,你可以安全地读取或修改它们。

// 在子项目的 build.gradle 中
this.afterEvaluate { project ->
    // 确保 android 插件已经应用
    if (project.plugins.hasPlugin('com.android.application')) {
        android {
            defaultConfig {
                // 动态设置版本号
                versionCode = getVersionCodeFromCI()
            }
        }
    }
    println "${project.name} 配置完成"
}

避坑指南:我曾经在 beforeEvaluate 里尝试读取 android 配置,结果报错说找不到。原因很简单——android 插件还没应用呢。所以记住:beforeEvaluate 适合做“准备工作”,afterEvaluate 适合做“收尾工作”。

5.3 执行阶段的 Hook

执行阶段的 Hook 主要围绕 Task 的执行过程。

Hook 方法 触发时机 使用场景
gradle.taskGraph.beforeTask 每个 Task 执行前 记录日志、检查前置条件
gradle.taskGraph.afterTask 每个 Task 执行后 统计耗时、发送通知
buildFinished 整个构建过程结束后 清理资源、生成报告

来看一个实际例子。我在团队里用 beforeTaskafterTask 来统计每个任务的执行时间:

// 在根项目的 build.gradle 中
gradle.taskGraph.beforeTask { task ->
    task.ext.startTime = System.currentTimeMillis()
    println "开始执行:${task.path}"
}

gradle.taskGraph.afterTask { task, state ->
    def duration = System.currentTimeMillis() - task.ext.startTime
    println "${task.path} 执行完毕,耗时 ${duration}ms"
    if (state.failure) {
        println "❌ ${task.path} 执行失败:${state.failure.message}"
    }
}

六、完整的生命周期流程图

下面这张图把 Hook 方法和三个阶段对应起来,方便你对照理解。

Gradle 生命周期与 Hook 对应关系 初始化阶段 Hook 方法 settingsEvaluated projectsLoaded 配置阶段 Hook 方法 project.beforeEvaluate project.afterEvaluate gradle.projectsEvaluated gradle.taskGraph.whenReady 执行阶段 Hook 方法 gradle.taskGraph.beforeTask gradle.taskGraph.afterTask buildFinished 📌 使用建议: 1. 初始化阶段:适合做项目级别的动态调整(如按环境包含/排除模块) 2. 配置阶段:适合做插件配置、Task 动态生成、属性注入 3. 执行阶段:适合做日志记录、性能监控、构建后处理 4. 尽量把耗时逻辑放在执行阶段,避免拖慢配置阶段

七、总结

Gradle 的生命周期其实不复杂,说白了就是“先看谁参与、再画依赖图、最后干活”。但真正用好它,需要你对每个阶段的 Hook 方法有清晰的认识。

我个人习惯在项目里这样用:

  • settings.gradle 里用 settingsEvaluated 做环境判断
  • 根项目的 build.gradle 里用 projectsEvaluated 做全局检查
  • 子项目的 build.gradle 里用 afterEvaluate 做插件配置的收尾
  • 执行阶段beforeTaskafterTask 做性能监控

嗯,记住一点:Hook 方法不是越多越好。滥用 Hook 会让构建逻辑变得难以追踪。我见过一个项目,build.gradle 里密密麻麻全是 afterEvaluate,最后排查问题花了整整两天。所以,只在真正需要的地方使用 Hook,保持构建脚本的简洁和可读性。


公众号:蓝海资料掘金营,微信deep3321