深入理解gradle中的task

简介

在之前的文章中,我们讲到了如何使用gradle创建一个简单的task,以及task之间怎么依赖,甚至使用了程序来创建task。在本文中,我们会更加深入的去了解一下gradle中的task。

定义task

定义一个task可以有很多种方式,比如下面的使用string作为task的名字:

  1. task('hello') {
  2. doLast {
  3. println "hello"
  4. }
  5. }
  6. task('copy', type: Copy) {
  7. from(file('srcDir'))
  8. into(buildDir)
  9. }

还可以使用tasks容器来创建:

  1. tasks.create('hello') {
  2. doLast {
  3. println "hello"
  4. }
  5. }
  6. tasks.create('copy', Copy) {
  7. from(file('srcDir'))
  8. into(buildDir)
  9. }

上面的例子中,我们使用tasks.create方法,将新创建的task加到tasks集合中。

我们还可以使用groovy特有的语法来定义一个task:

  1. task(hello) {
  2. doLast {
  3. println "hello"
  4. }
  5. }
  6. task(copy, type: Copy) {
  7. from(file('srcDir'))
  8. into(buildDir)
  9. }

tasks 集合类

上面我们在创建task的时候,使用了tasks集合类来创建task。

实际上,tasks集合类是一个非常有用的工具类,我们可以使用它来做很多事情。

直接在build文件中使用tasks,实际上是引用了TaskContainer的一个实例对象。我们还可以使用 Project.getTasks() 来获取这个实例对象。

我们看下TaskContainer的定义:

  1. public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainObjectContainer<Task>

从定义上,我们可以看出TaskContainer是一个task的集合和域对象的集合。

taskContainer中有四类非常重要的方法:

第一类是定位task的方法,有个分别是findByPath和getByPath。两个方法的区别就是findByPath如果没找到会返回null,而getByPath没找到的话会抛出UnknownTaskException。

看下怎么使用:

  1. task hello
  2. println tasks.getByPath('hello').path
  3. println tasks.getByPath(':hello').path

输出:

  1. :hello
  2. :hello

第二类是创建task的方法create,create方法有多种实现,你可以直接通过名字来创建一个task:

  1. task('hello') {
  2. doLast {
  3. println "hello"
  4. }
  5. }

也可以创建特定类型的task:

  1. task('copy', type: Copy) {
  2. from(file('srcDir'))
  3. into(buildDir)
  4. }

还可以创建带参数的构造函数的task:

  1. class CustomTask extends DefaultTask {
  2. final String message
  3. final int number
  4. @Inject
  5. CustomTask(String message, int number) {
  6. this.message = message
  7. this.number = number
  8. }
  9. }

上面我们为CustomTask创建了一个带参数的构造函数,注意,这里需要带上@javax.inject.Inject注解,表示我们后面可以传递参数给这个构造函数。

我们可以这样使用:

  1. tasks.create('myTask', CustomTask, 'hello', 42)

也可以这样使用:

  1. task myTask(type: CustomTask, constructorArgs: ['hello', 42])

第三类是register,register也是用来创建新的task的,不过register执行的是延迟创建。也就是说只有当task被需要使用的时候才会被创建。

我们先看一个register方法的定义:

  1. TaskProvider<Task> register​(String name,
  2. Action<? super Task> configurationAction)
  3. throws InvalidUserDataException

可以看到register返回了一个TaskProvider,有点像java多线程中的callable,当我们调用Provider.get()获取task值的时候,才会去创建这个task。

或者我们调用TaskCollection.getByName(java.lang.String)的时候也会创建对应的task。

最后一类是replace方法:

  1. Task replace​(String name)
  2. <T extends Task> T replace​(String name,
  3. Class<T> type)

replace的作用就是创建一个新的task,并且替换掉同样名字的老的task。

Task 之间的依赖

task之间的依赖关系是通过task name来决定的。我们可以在同一个项目中做task之间的依赖:

  1. task hello {
  2. doLast {
  3. println 'Hello www.flydean.com!'
  4. }
  5. }
  6. task intro {
  7. dependsOn hello
  8. doLast {
  9. println "I'm flydean"
  10. }
  11. }

也可以跨项目进行task的依赖,如果是跨项目的task依赖的话,需要制定task的路径:

  1. project('project-a') {
  2. task taskX {
  3. dependsOn ':project-b:taskY'
  4. doLast {
  5. println 'taskX'
  6. }
  7. }
  8. }
  9. project('project-b') {
  10. task taskY {
  11. doLast {
  12. println 'taskY'
  13. }
  14. }
  15. }

或者我们可以在定义好task之后,再处理task之间的依赖关系:

  1. task taskX {
  2. doLast {
  3. println 'taskX'
  4. }
  5. }
  6. task taskY {
  7. doLast {
  8. println 'taskY'
  9. }
  10. }

还可以动态添加依赖关系:

  1. task taskX {
  2. doLast {
  3. println 'taskX'
  4. }
  5. }
  6. // Using a Groovy Closure
  7. taskX.dependsOn {
  8. tasks.findAll { task -> task.name.startsWith('lib') }
  9. }
  10. task lib1 {
  11. doLast {
  12. println 'lib1'
  13. }
  14. }
  15. task lib2 {
  16. doLast {
  17. println 'lib2'
  18. }
  19. }
  20. task notALib {
  21. doLast {
  22. println 'notALib'
  23. }
  24. }

定义task之间的顺序

有时候我们的task之间是有执行顺序的,我们称之为对task的排序ordering。

先看一下ordering和dependency有什么区别。dependency表示的是一种强依赖关系,如果taskA依赖于taskB,那么执行taskA的时候一定要先执行taskB。

而ordering则是一种并不太强列的顺序关系。表示taskA需要在taskB之后执行,但是taskB不执行也可以。

在gradle中有两种order:分别是must run after和should run after。

taskA.mustRunAfter(taskB)表示必须遵守的顺序关系,而taskA.shouldRunAfter(taskB)则不是必须的,在下面两种情况下可以忽略这样的顺序关系:

第一种情况是如果shouldRunAfter引入了order循环的时候。

第二种情况是如果在并行执行的情况下,task所有的依赖关系都已经满足了,那么也会忽略这个顺序。

我们看下怎么使用:

  1. task taskX {
  2. doLast {
  3. println 'flydean.com'
  4. }
  5. }
  6. task taskY {
  7. doLast {
  8. println 'hello'
  9. }
  10. }
  11. taskY.mustRunAfter taskX
  12. //taskY.shouldRunAfter taskX

给task一些描述

我们可以给task一些描述信息,这样我们在执行gradle tasks的时候,就可以查看到:

  1. task copy(type: Copy) {
  2. description 'Copies the resource directory to the target directory.'
  3. from 'resources'
  4. into 'target'
  5. include('**/*.txt', '**/*.xml', '**/*.properties')
  6. }

task的条件执行

有时候我们需要根据build文件中的某些属性来判断是否执行特定的task,我们可以使用onlyIf :

  1. task hello {
  2. doLast {
  3. println 'www.flydean.com'
  4. }
  5. }
  6. hello.onlyIf { !project.hasProperty('skipHello') }

或者我们可以抛出StopExecutionException异常,如果遇到这个异常,那么task后面的任务将不会被执行:

  1. task compile {
  2. doLast {
  3. println 'We are doing the compile.'
  4. }
  5. }
  6. compile.doFirst {
  7. if (true) { throw new StopExecutionException() }
  8. }
  9. task myTask {
  10. dependsOn('compile')
  11. doLast {
  12. println 'I am not affected'
  13. }
  14. }

我们还可以启动和禁用task:

  1. myTask.enabled = false

最后我们还可以让task超时,当超时的时候,执行task的线程将会被中断,并且task将会被标记为failed。

如果我们想继续执行,那么可以使用 --continue。

注意, 只有能够响应中断的task,timeout才有用。

  1. task hangingTask() {
  2. doLast {
  3. Thread.sleep(100000)
  4. }
  5. timeout = Duration.ofMillis(500)
  6. }

task rule

如果我们想要给某些task定义一些规则,那么可以使用tasks.addRule:

  1. tasks.addRule("Pattern: ping<ID>") { String taskName ->
  2. if (taskName.startsWith("ping")) {
  3. task(taskName) {
  4. doLast {
  5. println "Pinging: " + (taskName - 'ping')
  6. }
  7. }
  8. }
  9. }

上我们定义了一个rule,如果taskName是以ping开头的话,那么将会输出对应的内容。

看下运行结果:

  1. > gradle -q pingServer1
  2. Pinging: Server1

我还可以将这些rules作为依赖项引入:

  1. task groupPing {
  2. dependsOn pingServer1, pingServer2
  3. }

Finalizer tasks

和java中的finally一样,task也可以指定对应的finalize task:

  1. task taskX {
  2. doLast {
  3. println 'taskX'
  4. }
  5. }
  6. task taskY {
  7. doLast {
  8. println 'taskY'
  9. }
  10. }
  11. taskX.finalizedBy taskY
  12. > gradle -q taskX
  13. taskX
  14. taskY

finalize task是一定会被执行的,即使上面的taskX中抛出了异常。

总结

以上就是gradle中task的详解,希望大家能够喜欢。

本文已收录于 http://www.flydean.com/gradle-task-in-depth/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

深入理解gradle中的task的更多相关文章

  1. 【三】Gradle中的Task

    gradle中,最经常被使用的,一个task,一个是dependencies 1.Task声明 task默认是DefaultTask类, Task中有两个属性 group description,最佳 ...

  2. 理解Flink中的Task和SUBTASK

    1.概念 Task(任务):Task是一个阶段多个功能相同的subTask 的集合,类似于Spark中的TaskSet. subTask(子任务):subTask是Flink中任务最小执行单元,是一个 ...

  3. Gradle中的闭包

    Gradle是基于Groovy的DSL基础上的构建工具,Gradle中的闭包,其原型上实际上即Groovy中闭包.而在表现形式上,其实,Gradle更多的是以约定和基于约定基础上的配置去展现.但本质上 ...

  4. storm源码之理解Storm中Worker、Executor、Task关系 + 并发度详解

    本文导读: 1 Worker.Executor.task详解 2 配置拓扑的并发度 3 拓扑示例 4 动态配置拓扑并发度 Worker.Executor.Task详解: Storm在集群上运行一个To ...

  5. 深入理解gradle编译-Android基础篇

    深入理解gradle编译-Android基础篇 导读 Gradle基于Groovy的特定领域语言(DSL)编写的一种自动化建构工具,Groovy作为一种高级语言由Java代码实现,本文将对Gradle ...

  6. 十分钟理解Gradle

    一.什么是Gradle 简单的说,Gradle是一个构建工具,它是用来帮助我们构建app的,构建包括编译.打包等过程.我们可以为Gradle指定构建规则,然后它就会根据我们的“命令”自动为我们构建ap ...

  7. Gradle 1.12 翻译——第十七章. 从 Gradle 中调用 Ant

    有关其他已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或访问:http://gradledoc.qiniudn.com ...

  8. Gradle之Android Gradle Plugin 主要 Task 分析(三)

    [Android 修炼手册]Gradle 篇 -- Android Gradle Plugin 主要 Task 分析 预备知识 理解 gradle 的基本开发 了解 gradle task 和 plu ...

  9. gradle中使用嵌入式(embedded) tomcat, debug 启动

    在gradle项目中使用embedded tomcat. 最开始部署项目需要手动将web项目打成war包,然后手动上传到tomcat的webapp下,然后启动tomcat来部署项目.这种手动工作通常还 ...

随机推荐

  1. 微服务网关2-搭建Gateway服务

    一.创建父模块infrastructure 1.创建模块 在guli_parent下创建普通maven模块 Artifact:infrastructure 2.删除src目录 二.创建模块api_ga ...

  2. 使用Azure Runbook 发送消息到Azure Storage Queue

    客户需要定时发送信息到Azure Storage Queue,所以尝试使用Azure Runbook实现这个需求. 首先新增一个Azure Automation Account的资源. 因为要使用Az ...

  3. 如何将python中pip源设置为国内源

    1.Windows Python的学习过程中,往往会学习到很多库,而安装各类库的时候,往往不尽人意,下载速度从几KB到十几KB.甚至下载到一半还超时报错.这都是因为pip源是访问国外的官方源,如果需要 ...

  4. linux--关于JVM CPU资源占用过高的问题排查

    一.背景: 先执行一个java程序里面开了两个线程分别都在while循环做打印操作. # java -cp ./test-threads.jar com.spiro.Main 二.现象: 通过top命 ...

  5. 流量染色与gRPC服务托管 微服务协作开发、灰度发布之流量染色 灰度发布与流量染色

    大规模微服务场景下灰度发布与流量染色实践 https://mp.weixin.qq.com/s/UBoRKt3l91ffPagtjExmYw [go-micro]微服务协作开发.灰度发布之流量染色 - ...

  6. 洛谷P2573

    Description \(n\) 个点,有各自的高度. \(m\) 条道路,有各自的长度,每条可连接两个点. 规定只能从高点走向低点,可以回到原来的某个位置走不同的道路. 求在行走道路尽量短的情况下 ...

  7. noip 注意事项 (个人向)

    目录 非常重要 对拍 空间 极限数据 模数,YES/NO等大小写 个人 考场 神仙 czdzx 说要写,我也来写 非常重要 对拍 空间 极限数据 模数,YES/NO等大小写 个人 养身体,不要紧张,不 ...

  8. jQuery基础介绍

    最近在学习JavaScript,当学习Javascript之后,不得不学习的肯定是jQuery了,所以开始利用网络的便捷浏览各大博客寻找可学习的资源.这篇博客关于jQuery的学习让我有很多收获,也明 ...

  9. Java,Scala:JDBCUtil,MySqlUtil,PhoenixJDBC

    Java,Scala:JDBCUtil,MySqlUtil,PhoenixJDBC pom.xml添加依赖 Java:方式一(亲测实用) 方式二:Scala 方式三:Java PhoenixJDBCU ...

  10. java架构《并发线程高级篇三》

    本章主要介绍和讲解concurrent.util里面的常用的工具类. 一.CountDownLatch使用:(用于阻塞主线程) 应用场景 :通知线程休眠和运行的工具类,是wait和notify的升级版 ...