在上一篇文章一文了解 Gradle 配置文件 中我们介绍了 Gradle 的配置文件。但是你是否知道这个配置文件是什么时候被读取的呢?要回答这个问题,就需要了解到本篇文章介绍的 Gradle 的生命周期。
生命周期
其实 Gradle 的生命周期并不复杂。它只有三个阶段,分别是 Initialization (初始化)、Configuration (配置) 和 Execution (执行),如下图所示:
从图中可以看到:
- 在 初始化阶段(Initialization),主要是解析
settings.gradle(.kts)文件,并创建Settings实例。在解析settings.gradle(.kts)文件时,Gradle 根据该文件来决定构建应该包含哪些项目,并为需要构建的项目创建Project实例。 - 配置阶段(Configuration): 则会解析参与构建项目的
build.gradle(.kts)文件,并根据 build.gradle 文件来创建构建需要的 Task 有向无环图(DAG) - 执行阶段(Execution) 则会执行在配置阶段构建好的任务图
现在我们就可以回答上面提出的问题:配置文件是什么时候被读取的呢?答案是,在初始化阶段会读取 settings.gradle 文件;而在配置阶段则会读取参与构建的 build.gradle 文件(包括根build.gradle 文件)。
这里需要注意的是,虽然 Project 对象是在初始化阶段创建的,但是由于此时还没有解析 build.gradle 文件,所以这时的 Project 对象只有项目名,以及路径等基本信息。
监听生命周期
有了生命周期,当然少不了监听它来做一些自己的操作。Gradle 提供了很多hook来监听它的生命周期,可以很方便的满足我们的需求。
初始化阶段
在初始化阶段,有 settingsEvaluated 和 projectsLoaded 这两个 hook 函数。它们调用的时机分别是 settings 对象创建 以及 project 对象被创建。代码示例如下:
// setting.gradle 文件
println("解析 settings 文件")
gradle.settingsEvaluated {
println("settings 对象创建完成")
}
gradle.projectsLoaded {
println("project 对象被创建完了")
}
两个 hook 函数的图示如下:
除此之外还有一个 gradle.allprojects 函数,它会在每一个 project 被创建时调用。
// setting.gradle 文件
gradle.allprojects {
println("${this.name} create")
}
你可能好奇这个 gradle 对象是什么时候创建的,其实也是在初始化阶段。在这个阶段最开始,Gradle 会执行Init Script来读取一些通用的配置信息等,然后才是读取 setting 文件。不过这个算是 Gradle 的内部流程,和我们的构建关系不大,了解即可。
配置阶段
在配置阶段,hook函数主要是 beforeEvaluate 和 afterEvaluate,调用时机如下图示所示:
beforeEvaluate 和 afterEvaluate 会分别在每一个 build.gradle 文件的解析前和解析后执行。代码示例如下:
// app模块下的build.gradle
// 不会执行,因为执行到这里的时候,beforeEvaluate 的时机已经过去了
project.beforeEvaluate {
println("app beforeEvaluate 执行 ${this.name}")
}
project.afterEvaluate {
println("app afterEvaluate 执行 ${this.name}")
}
从上图可以看到,由于 beforeEvaluate 会在解析 build.gradle 文件前执行,因此在 build.gradle 中注册 beforeEvaluate hook是不会执行。如果你想要监听这个 beforeEvaluate
生命周期,你可以在使用 gradle.allprojects 函数,代码示例如下:
gradle.allprojects {
// 在创建 project 时执行
println("allprojects")
beforeEvaluate {
println("build.gradle开始解析,对象是 = ${project.name}")
}
afterEvaluate {
println("build.gradle解析完成,对象是 = ${project.name}")
}
}
如果需要监听所有的 build.gradle 文件解析完成,可以使用 projectsEvaluated 。代码示例如下:
gradle.projectsEvaluated {
println("所有build.gralde都解析完了")
}
还有一点需要注意,在不同文件中的 allprojects 是不一样的,代码示例如下:
// 任意文件下的
gradle.allprojects{} // 表示所有的 project
// 根目录下的 build.gradle
allprojects{} // 表示所有的 project
// app 目录下的 build.gradle
allprojects{} // 表示app 以及它的子模块的 project;与它同级的模块是不包括的
执行阶段
在Gradle 7以前,我们可以使用 TaskActionListener 来监听任务action的开始和结束;可以使用 TaskExecutionListener 来监听任务执行的开始和结束;以及使用 gradle.buildFinished 来监听构建结束。但是由于在配置缓存的情况下,执行逻辑不一致,因此它们都被废弃了。
在 Gradle 8中,这些接口被构建服务取代,并在需要时注册以接收有关任务执行的信息。使用数据流操作来处理构建结果,而不是buildFinished监听器。代码示例如下:
public abstract class TaskEventsService implements BuildService<BuildServiceParameters.None>,
OperationCompletionListener {
@Override
public void onFinish(FinishEvent finishEvent) {
if (finishEvent instanceof TaskFinishEvent) {
// Handle task finish event...
}
}
}
Provider<TaskEventsService> serviceProvider =
project.getGradle().getSharedServices().registerIfAbsent(
"taskEvents", TaskEventsService.class, spec -> {});
getEventsListenerRegistry().onTaskCompletion(serviceProvider);
其他
在 Gradle 中还有一个 addBuildListener 方法来监听生命周期,其效果和上面的hook是一样的,代码示例如下:
// 和上面的hook效果是一样的
gradle.addBuildListener(object : BuildListener {
override fun settingsEvaluated(result: Settings) {
println("settings 对象创建完成")
}
override fun projectsLoaded(result: Gradle) {
println("project 对象被创建完了")
}
override fun projectsEvaluated(result: Gradle) {
println("所有build.gralde都解析完了")
}
// 被废弃
override fun buildFinished(result: BuildResult) {
TODO("Not yet implemented")
}
})