以下代码使用 Gradle 5.4.1
1. 概念
Gradle 提供了一个构建任意类型项目的框架,如果你需要构建某个具体的项目,比如 Spring 或者 Android,那么就需要提供管理这种项目类型的 Plugin。Android 就是 Android Gradle Plugin(AGP),AGP 需要和特性版本的 Gradle 配合使用:Android Gradle 插件版本说明。这些 Plugin 中有许多 Task,它们共同为项目构建提供了生命周期的管理。
2. Plugin 和 Task
2.1 一个简单的 Task
Project 为 Task 的创建和执行提供了上下文,所有的 Plugin 要么向 Project 中添加用于配置的 Property,要么向 Project 中添加不同的 Task。一个 Task 表示一个逻辑上较为独立的执行过程,比如编译 Java 源码,拷贝文件等。来看一个 Hello world Task:
// 通过 gradle init 来生成 build.gradle
tasks.register("hello") {
doLast {
println 'Hello, World!'
}
}
上面的代码往 Gradle 的 tasks
中注册了一个新的名叫 hello 的 Task,并且这个 Task 有个 Action,它打印 Hello, World! 到控制台。
如果执行命令:gradle tasks --all
,那么就会看到所有的 Task:
我们可以看到,Gradle 其实已经内置了一些基本的 Task,这些基本的 Task 与我们的 hello 相比,多了许多说明。我们可以通过往脚本中加入以下语句达到相同效果:
tasks.register("hello") {
group = 'Welcome'
description = 'Produces a greeting'
doLast {
println 'Hello, World!'
}
}
2.2 一个自定义 Task
class Greeting extends DefaultTask {
String message
String recipient
@TaskAction
void sayGreeting() {
println "${message}, ${recipient}!"
}
}
tasks.register("hello", Greeting) {
group = 'Welcome'
description = 'Produces a greeting'
message = 'Hello'
recipient = 'World'
}
通过继承 DefaultTask
来自定义 Greeting
Task。它跟 2.1 中示例一样,提供两个配置项:group
和 description
。通过注解 @TaskAction
来实现 Greeting
的 action。
2.1 中的写法就像是一个匿名内部类,而 2.2 的写法则更像是声明一个具体类
Greeting
,并实例化一个hello
的实例。
因为 Greeting
是一个 Task 的类声明,所以我们可以再写一个德语版本的实例:
tasks.register("gutenTag", Greeting) {
group = 'Welcome'
description = 'Produces a German greeting'
message = 'Guten Tag'
recipient = 'Welt'
}
2.3 另一种写法
关于 Task 的实例化,我们还有另外一种更 groovy style 的写法:
task helloWorld(type: Greeting) {
group = 'Welcome'
description = 'other writing style'
message = 'Hello'
recipient = 'world'
}
那么 2.3 中的写法与之前的写法有什么不同呢?
- 其实,每个
Project
中都有一个TaskContainer
,Project
的实例维护了一个TaskContainer
类型的属性tasks
。我们通过查 TaskContainer API 可以知道,当我们写tasks.register()
来创建一个 Task 实例时,其实是调用的TaskContainer.register()
。 -
2.3 的写法中,
task
其实是 build.gradle 文件的关键字。how-to-translate-task-keyword-in-dsl-into-groovy-call,通过 Compile-time metaprogramming and AST transformations,最后将其转换为调用Project#task(Map<String, ?> args, String name, Closure configureClosure)
。这其中还涉及到 groovy 的一个属性:它会收集方法调用中所有有名参数,将这些参数转换成一个Map
,再去调用这个方法的第一个参数为Map
的重载方法。因此task
关键字如果带参数的话,最后将会被转换并调用Project#task(Map<String, ?> args, String name, Closure configureClosure)
。
3. 内建 Task
我们还可以继承一个内建 Task,比如 Copy
:
task copyFile(type: Copy) {
from 'srcFile'
into 'destFile'
}
以上 copyFile
将 srcFile
文件夹中的内容拷贝到 destFile
文件夹中。这里的两个文件夹都是相对于当前 Project
而言的,即 build.gradle 文件所在的目录。
Task 之间可以存在依赖关系,比如 taskA
依赖于 taskB
,那么在执行 taskA
时,Gradle 会先执行 taskB
,然后再执行 taskA
。声明 Task 依赖关系的一种方式是在定义一个 Task 的时候:
task taskA(dependsOn: taskB) {
//do something
}
task copyFile(type: Copy, dependsOn: hello) {
from 'srcFile'
into 'destFile'
}
那么上面重新定义的 copyFile
在执行前,Gradle 会先执行 hello
。
3. Task 的生命周期概览
上面提到的 description
我们还可以在闭包中设置它。
task hello8 {
doLast {
println description
}
}
hello8 {
description = "this is hello8"
}
上面代码虽然将 description
的配置放在了最后,但是执行 gradle hello8 仍然可以看到 this is hello8
被正确打印。这是因为,Gradle 在执行 Task 时分为两个阶段,首先是配置阶段,然后才是实际执行阶段。所以在执行 hello8 之前,Gradle 会扫描整个 build.gradle 文件,将 hello8
的 description
设置为 this is hello8
,然后执行 hello8
。
网友评论