Task详解

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() 方法,一直报错,无法在代码中使其运行。

希望有解决过的同学讲一哈,多谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tʀᴜsᴛ³⁴⁵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值