1,Task 的创建
//直接通过 task 函数创建
task("helloTask") {
println '--------- i am helloTask'
}
//通过 TaskContainer 去创建 Task
this.tasks.create(name: "helloTask2") {
println '--------- i am helloTask2'
}
以上两种方式创建的 Task 没有任何区别,
this.tasks 返回的就是 TaskContainer 类,Task 的容器类,上面两种方式创建的 Task 都会被添加当前 project 的 TaskContainer 类中,TaskContainer 是用来管理当前 project 中所有的 Task。既然是用来管理的,他肯定可以对 Taks 进行一些操作,如下:
public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainObjectContainer<Task> {
//在指定路径下查找 task ,没有返回 null
@Nullable
Task findByPath(String path);
//同上,没有抛出异常
Task getByPath(String path) throws UnknownTaskException;
//非常多的创建方法
Task create(Map<String, ?> options) throws InvalidUserDataException;
Task create(Map<String, ?> options, Closure configureClosure) throws InvalidUserDataException;
Task create(String name, Closure configureClosure) throws InvalidUserDataException;
Task create(String name) throws InvalidUserDataException;
}
无论使用那种方式创建 Task ,最终 TaskContainer 会将所有的 Task 在配置阶段构成一个有向无环图。通过这个图 gradle 就能知道 task 的类别和执行顺序。
2,Task配置
可以在创建的时候直接配置组合描述信息,如下:
task helloTask(group: '345', description: 'Task Study') {
println '--------- i am helloTask'
}
也可以在闭包中进行设置描述信息:
this.tasks.create(name: "helloTask2") {
setGroup("345")
setDescription("Task Study")
println '--------- i am helloTask2'
}
第一种看起来比较简洁,指定 group 后 ,就会将 task 放在对应的组中。description 就相当于是注释。指定分组后,点击 as 右上角的 Gradle ,打开指定 task 的 module。就会显示当前 project 的所有 task。如果指定了分组,则就会有对应的文件夹,如果没有就会放在 other 中,如下:
当然 task 不会只能配置上面两种信息。打开 Task 类,如下:
public interface Task extends Comparable<Task>, ExtensionAware {
//名字
String TASK_NAME = "name";
//描述
String TASK_DESCRIPTION = "description";
//组
String TASK_GROUP = "group";
//类型
String TASK_TYPE = "type";
//指定当前 task 依赖于那些 task
String TASK_DEPENDS_ON = "dependsOn";
//重写 task
String TASK_OVERWRITE = "overwrite";
//配置要执行的逻辑
String TASK_ACTION = "action";
}
3,让 Task 执行在 执行阶段
首先我们有运行上面的例子 gradlew helloTask ,如下:
--------- i am helloTask
--------- i am helloTask2
这两个都执行了。为啥呢?他们是在 配置阶段执行的,并不是执行阶段,所以他们都会被执行。下面我们就看一下执行阶段。
执行阶段是 gradle 生命周期的第三阶段。也只有 Task 才能在第三阶段中执行,要将 Task 执行在 执行阶段,有两个方法,分别是 doFirst 和 doLast ,通过 doFirst 可以为已经有的 task 之前添加相应的逻辑,doLast 则是之后了,下面看一下简单的使用:
task helloTask(group: '345', description: 'Task Study') {
// doFirst 执行在 执行阶段
doFirst {
println '--------- i am helloTask --1'
}
//可执行多次
doFirst {
println '--------- i am helloTask --2'
}
doLast{
println "last"
}
}
helloTask.doFirst {
//可定义在外面
println '--------- i am helloTask --3'
}
打印结果:
> Task :app:helloTask
--------- i am helloTask --3
--------- i am helloTask --2
--------- i am helloTask --1
last
在配置阶段执行的时候是不会打印 Task :app:helloTask 这句话的。这里的执行顺序是从外部的开始执行,最后执行的是 doLast 。
小例子:计算 Build 中 task 的执行时长
// 计算 build 计算时长
def startBuildTime, endBuildTime
this.afterEvaluate {
Project p ->
def preBuildTask = p.tasks.getByName('preBuild')
preBuildTask.doFirst {
startBuildTime = System.currentTimeMillis()
println "this start time is :" + startBuildTime
}
def buildTask = p.tasks.getByName('build')
buildTask.doLast {
endBuildTime = System.currentTimeMillis()
println "this build time is :${endBuildTime - startBuildTime}"
}
}
使用命令 gradlew build 开始 build
执行结果:
> Task :app:preBuild
this start time is :1576560417687
> Task :app:build
this build time is :28783
其中 this.afterEvaluate 是生命周期中的方法,绘制配置阶段执行所有的 task 配置完成后执行,接着就是获取 preBuild task,他是第一个开始执行的 task,不信你可以看一下控制台的log。在 preBuild 的 doFirst 中获取开始时间,在 build task 完成后获取结束时间即可。
4,Task 执行顺序
看上面的小例子:我们执行的命令是 gradlew build ,那为啥先执行 preBuild 呢!这就和 Task 的执行顺序有关了。
-
强依赖方式
为 task 指定多个依赖性的 task。这样当前的 task 必须等依赖的 task 执行完才可以执行
task TaskX { doLast { println 'taskX' } } task TaskY { doLast { println 'taskY' } } //依赖 taskX ,taskY task TaskZ(dependsOn: [TaskX, TaskY]) { doLast { println 'taskZ' } } // TaskZ.denpendsOn(TaskX, TaskY) 这种也可以
执行结果:
> Task :app:TaskX taskX > Task :app:TaskY taskY > Task :app:TaskZ taskZ
如果在最开始不知道要依赖那些 task,可以通过下面这种方式:
task TaskX { doLast { println 'taskX' } } task TaskY { doLast { println 'taskY' } } //依赖 taskX ,taskY task Test { dependsOn this.tasks.findAll { task -> //测试此字符串是否以指定的前缀开始。 return task.name.startsWith('Task') } doLast { println 'Test' } }
结果如下:
> Task :app:TaskX taskX > Task :app:TaskY taskY > Task :app:Test Test
-
通过 api 来指定顺序
//执行顺序指定 task taskX { doLast { println 'taskX' } } task taskY { //指定执行在 TaskX 之后 mustRunAfter taskX doLast { println 'taskY' } } task taskZ { //指定执行在 TaskY 之后 mustRunAfter taskY doLast { println 'taskZ' } }
输入命令:gradlew taskX taskZ TASKZ 结果如下:
> Task :app:taskX taskX > Task :app:taskY taskY > Task :app:taskZ taskZ
最后看一个例子:
ext {
versionName = '1.0.1'
versionCode = 1
versionInfo = '第1个版本'
destFile = file('releases.json')
if (destFile != null && !destFile.exists()) {
destFile.createNewFile()
}
}
上面的 ext 不会陌生吧,他是扩展属性。内部定义了一写属性,创建了一个文件 releases.json
接着定义一个类,来保存这些属性:
class VersionMsg {
String versionName
String versionCode
String versionInfo
}
然后创建一个 Task。用来给文件中写入信息:
task writeTask {
//为 task 指定输入
inputs.property('versionName', this.versionName)
inputs.property('versionCode', this.versionCode)
inputs.property('versionInfo', this.versionInfo)
//为 task 指定输出
outputs.file this.destFile
doLast {
//获取输入让的信息,返回 map
def map = inputs.getProperties()
File file = outputs.getFiles().getSingleFile()
def versionMsg = new VersionMsg(map)
def json = JsonOutput.toJson(versionMsg)
if (file.text != null && file.text.size() >= 0) {
def lines = file.readLines()
def lengths = lines.size() //一共多少行
file.withWriter {
writer ->
if (lengths == 0) {
writer.append("[")
//将json串格式化后写入
writer.append("${JsonOutput.prettyPrint(json)}\n")
} else {
for (int i = 0; i < lines.size(); i++) {
if (i == lengths - 1) {
writer.append(",")
break
}
writer.append(lines[i] + "\n")
}
writer.append("${JsonOutput.prettyPrint(json)}\n")
}
writer.append("]")
}
}
}
}
inputs 和 outputs 是 Task 的属性,inputs 可以是任意类型的对象,而 outputs只能是文件(或文件夹)
上面这段代码首先是指定了输入和输出,然后获取输入的文件,并传入到 VersionMsg类中,接着将这个类转为 json 串。最后将 json 写入到文件中。
注意要使用 JsonOutput ,必须先导包,否则不能使用,如下:
import groovy.json.JsonOutput
最终执行 gradlew writeTask 命令,执行前请修改信息,写入的文件如下:
这就是输出的版本信息,其中 contentHash 和 originalClassName 是自动生成的。
在这个过程中也遇到了一个问题:在执行 Task 的时候,如果 Task 内涉及的内容没有发生任何变化,那么这个 task 不会执行。如果上面的执行完第三次后没有修改任何地方,在继续执行 Task 则不会执行。但是只要有任何修改,task 都会执行:如将 文件中的 json 传格式化一下,修改一下等,再次执行命令,则 task 就会执行。
到这里就完了,但是还是有些疑惑,在学习的过程中,看到可以使用 输入输出来指定执行的顺序,但是我却没弄出来。还有就是指定在 build Task 后执行,但是定义的 task 没有 execute() 方法,一直报错,无法在代码中使其运行。
这就是输出的版本信息,其中 contentHash 和 originalClassName 是自动生成的。
在这个过程中也遇到了一个问题:在执行 Task 的时候,如果 Task 内涉及的内容没有发生任何变化,那么这个 task 不会执行。如果上面的执行完第三次后没有修改任何地方,在继续执行 Task 则不会执行。但是只要有任何修改,task 都会执行:如将 文件中的 json 传格式化一下,修改一下等,再次执行命令,则 task 就会执行。
到这里就完了,但是还是有些疑惑,在学习的过程中,看到可以使用 输入输出来指定执行的顺序,但是我却没弄出来。还有就是指定在 build Task 后执行,但是定义的 task 没有 execute() 方法,一直报错,无法在代码中使其运行。
希望有解决过的同学讲一哈,多谢