在粒度性上,使用 Maven、Ant 等构建工具时,我们的源代码和构建脚本是独立的,而且我们也不知道其内部的处理是怎样的。但是,我们的 Gradle 则不同,它 从源代码的编译、资源的编译、再到生成 APK 的过程中都是一个接一个来执行的。
此外,Gradle 构建的粒度细化到了每一个 task 之中。并且它所有的 Task 源码都是开源的,在我们掌握了这一整套打包流程后,我们就可以通过去修改它的 Task 去动态改变其执行流程。例如 Tinker 框架的实现过程中,它通过动态地修改 Gradle 的打包过程生成 APK 的同时,也生成了各种补丁文件。
3、更好的扩展性
在扩展性上,Gradle 支持插件机制,所以我们可以复用这些插件,就如同复用库一样简单方便。
4、更强的兼容性
Gradle 不仅自身功能强大,而且它还能 兼容所有的 Maven、Ant 功能,也就是说,Gradle 吸取了所有构建工具的长处。
可以看到,Gradle 相比于其它构建工具,其好处不言而喻,而其 最核心的原因就是因为 Gradle 是一套编程框架。
二、Gradle 构建生命周期
Gradle 的构建过程分为 三部分:初始化阶段、配置阶段和执行阶段。其构建流程如下图所示:
下面分别来详细了解下它们。
1、初始化阶段
首先,在这个阶段中,会读取根工程中的 setting.gradle 中的 include 信息,确定有多少工程加入构建,然后,会为每一个项目(build.gradle 脚本文件)创建一个个与之对应的 Project 实例,最终形成一个项目的层次结构。 与初始化阶段相关的脚本文件是 settings.gradle,而一个 settings.gradle 脚本对应一个 Settings 对象,我们最常用来声明项目的层次结构的 include 就是 Settings 对象下的一个方法,在 Gradle 初始化的时候会构造一个 Settings 实例对象,以执行各个 Project 的初始化配置。
settings.gradle
在 settings.gradle 文件中,我们可以 在 Gradle 的构建过程中添加各个生命周期节点监听,其代码如下所示:
include ‘:app’
gradle.addBuildListener(new BuildListener() {
void buildStarted(Gradle var1) {
println ‘开始构建’
}
void settingsEvaluated(Settings var1) {
// var1.gradle.rootProject 这里访问 Project 对象时会报错,
// 因为还未完成 Project 的初始化。
println ‘settings 评估完成(settings.gradle 中代码执行完毕)’
}
void projectsLoaded(Gradle var1) {
println ‘项目结构加载完成(初始化阶段结束)’
println ‘初始化结束,可访问根项目:’ + var1.gradle.rootProject
}
void projectsEvaluated(Gradle var1) {
println ‘所有项目评估完成(配置阶段结束)’
}
void buildFinished(BuildResult var1) {
println '构建结束 ’
}
})
编写完相应的 Gradle 生命周期监听代码之后,我们就可以在 Build 输出界面看到如下信息:
Executing tasks: [clean, :app:assembleSpeedDebug] in project
/Users/quchao/Documents/main-open-project/Awesome-WanAndroid
settings评估完成(settins.gradle中代码执行完毕)
项目结构加载完成(初始化阶段结束)
初始化结束,可访问根项目:root project ‘Awesome-WanAndroid’
Configuration on demand is an incubating feature.
Configure project :app
gradlew version > 4.0
WARNING: API ‘variant.getJavaCompiler()’ is obsolete and has been
replaced with ‘variant.getJavaCompileProvider()’.
It will be removed at the end of 2019.
For more information, see
https://d.android.com/r/tools/task-configuration-avoidance.
To determine what is calling variant.getJavaCompiler(), use
-Pandroid.debug.obsoleteApi=true on the command line to display more
information.
skip tinyPicPlugin Task!!!
skip tinyPicPlugin Task!!!
所有项目评估完成(配置阶段结束)
Task :clean UP-TO-DATE
:clean spend 1ms
…
Task :app:clean
:app:clean spend 2ms
Task :app:packageSpeedDebug
:app:packageSpeedDebug spend 825ms
Task :app:assembleSpeedDebug
:app:assembleSpeedDebug spend 1ms
构建结束
Tasks spend time > 50ms:
…
此外,在 settings.gradle 文件中,我们可以指定其它 project 的位置,这样就可以将其它外部工程中的 moudle 导入到当前的工程之中了。示例代码如下所示:
if (useSpeechMoudle) {
// 导入其它 App 的 speech 语音模块
include “speech”
project(":speech").projectDir = new File("…/OtherApp/speech")
}
2、配置阶段
配置阶段的任务是 执行各项目下的 build.gradle 脚本,完成 Project 的配置,与此同时,会构造 Task 任务依赖关系图以便在执行阶段按照依赖关系执行 Task。而在配置阶段执行的代码通常来说都会包括以下三个部分的内容,如下所示:
- 1)、build.gralde 中的各种语句。
- 2)、闭包。
- 3)、Task 中的配置段语句。
需要注意的是,执行任何 Gradle 命令,在初始化阶段和配置阶段的代码都会被执行。
3、执行阶段
在配置阶段结束后,Gradle 会根据各个任务 Task 的依赖关系来创建一个有向无环图,我们可以通过 Gradle 对象的 getTaskGraph 方法来得到该有向无环图 => TaskExecutionGraph,并且,当有向无环图构建完成之后,所有 Task 执行之前,我们可以通过 whenReady(groovy.lang.Closure) 或者 addTaskExecutionGraphListener(TaskExecutionGraphListener) 来接收相应的通知,其代码如下所示:
gradle.getTaskGraph().addTaskExecutionGraphListener(new
TaskExecutionGraphListener() {
@Override
void graphPopulated(TaskExecutionGraph graph) {
}
})
然后,Gradle 构建系统会通过调用 gradle <任务名> 来执行相应的各个任务。
4、Hook Gradle 各个生命周期节点
这里借用 Goe_H 的 Gradle 生命周期时序图来讲解一下 Gradle 生命周期的整个流程,如下图所示:
可以看到,整个 Gradle 生命周期的流程包含如下 四个部分:
- 1)、首先,解析 settings.gradle 来获取模块信息,这是初始化阶段。
- 2)、然后,配置每个模块,配置的时候并不会执行 task。
- 3)、接着,配置完了以后,有一个重要的回调 project.afterEvaluate,它表示所有的模块都已经配置完了,可以准备执行 task 了。
- 4)、最后,执行指定的 task 及其依赖的 task。
在 Gradle 构建命令中,最为复杂的命令可以说是 gradle build
这个命令了,因为项目的构建过程中需要依赖很多其它的 task。这里,我们以 Java 项目的构建过程看看它所依赖的 tasks 及其组成的有向无环图,如下所示: