关于java:深入理解gradle中的task

36次阅读

共计 4841 个字符,预计需要花费 13 分钟才能阅读完成。

简介

在之前的文章中,咱们讲到了如何应用 gradle 创立一个简略的 task,以及 task 之间怎么依赖,甚至应用了程序来创立 task。在本文中,咱们会更加深刻的去理解一下 gradle 中的 task。

定义 task

定义一个 task 能够有很多种形式,比方上面的应用 string 作为 task 的名字:

task('hello') {
    doLast {println "hello"}
}

task('copy', type: Copy) {from(file('srcDir'))
    into(buildDir)
}

还能够应用 tasks 容器来创立:

tasks.create('hello') {
    doLast {println "hello"}
}

tasks.create('copy', Copy) {from(file('srcDir'))
    into(buildDir)
}

下面的例子中,咱们应用 tasks.create 办法,将新创建的 task 加到 tasks 汇合中。

咱们还能够应用 groovy 特有的语法来定义一个 task:

task(hello) {
    doLast {println "hello"}
}

task(copy, type: Copy) {from(file('srcDir'))
    into(buildDir)
}

tasks 汇合类

下面咱们在创立 task 的时候,应用了 tasks 汇合类来创立 task。

实际上,tasks 汇合类是一个十分有用的工具类,咱们能够应用它来做很多事件。

间接在 build 文件中应用 tasks,实际上是援用了 TaskContainer 的一个实例对象。咱们还能够应用 Project.getTasks() 来获取这个实例对象。

咱们看下 TaskContainer 的定义:

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

从定义上,咱们能够看出 TaskContainer 是一个 task 的汇合和域对象的汇合。

taskContainer 中有四类十分重要的办法:

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

看下怎么应用:

task hello

println tasks.getByPath('hello').path
println tasks.getByPath(':hello').path

输入:

:hello
:hello

第二类是创立 task 的办法 create,create 办法有多种实现,你能够间接通过名字来创立一个 task:

task('hello') {
    doLast {println "hello"}
}

也能够创立特定类型的 task:

task('copy', type: Copy) {from(file('srcDir'))
    into(buildDir)
}

还能够创立带参数的构造函数的 task:

class CustomTask extends DefaultTask {
    final String message
    final int number

    @Inject
    CustomTask(String message, int number) {
        this.message = message
        this.number = number
    }
}

下面咱们为 CustomTask 创立了一个带参数的构造函数,留神,这里须要带上 @javax.inject.Inject 注解,示意咱们前面能够传递参数给这个构造函数。

咱们能够这样应用:

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

也能够这样应用:

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

第三类是 register,register 也是用来创立新的 task 的,不过 register 执行的是提早创立。也就是说只有当 task 被须要应用的时候才会被创立。

咱们先看一个 register 办法的定义:

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

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

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

最初一类是 replace 办法:

Task replace​(String name)
<T extends Task> T replace​(String name,
                           Class<T> type)

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

Task 之间的依赖

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

task hello {
    doLast {println 'Hello www.flydean.com!'}
}
task intro {
    dependsOn hello
    doLast {println "I'm flydean"}
}

也能够跨我的项目进行 task 的依赖,如果是跨我的项目的 task 依赖的话,须要制订 task 的门路:

project('project-a') {
    task taskX {
        dependsOn ':project-b:taskY'
        doLast {println 'taskX'}
    }
}

project('project-b') {
    task taskY {
        doLast {println 'taskY'}
    }
}

或者咱们能够在定义好 task 之后,再解决 task 之间的依赖关系:

task taskX {
    doLast {println 'taskX'}
}

task taskY {
    doLast {println 'taskY'}
}

还能够动静增加依赖关系:

task taskX {
    doLast {println 'taskX'}
}

// Using a Groovy Closure
taskX.dependsOn {tasks.findAll { task -> task.name.startsWith('lib') }
}

task lib1 {
    doLast {println 'lib1'}
}

task lib2 {
    doLast {println 'lib2'}
}

task notALib {
    doLast {println 'notALib'}
}

定义 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 所有的依赖关系都曾经满足了,那么也会疏忽这个程序。

咱们看下怎么应用:

task taskX {
    doLast {println 'flydean.com'}
}
task taskY {
    doLast {println 'hello'}
}
taskY.mustRunAfter taskX
//taskY.shouldRunAfter taskX

给 task 一些形容

咱们能够给 task 一些形容信息,这样咱们在执行 gradle tasks 的时候,就能够查看到:

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

task 的条件执行

有时候咱们须要依据 build 文件中的某些属性来判断是否执行特定的 task,咱们能够应用 onlyIf:

task hello {
    doLast {println 'www.flydean.com'}
}

hello.onlyIf {!project.hasProperty('skipHello') }

或者咱们能够抛出 StopExecutionException 异样,如果遇到这个异样,那么 task 前面的工作将不会被执行:

task compile {
    doLast {println 'We are doing the compile.'}
}

compile.doFirst {if (true) {throw new StopExecutionException() }
}
task myTask {dependsOn('compile')
    doLast {println 'I am not affected'}
}

咱们还能够启动和禁用 task:

myTask.enabled = false

最初咱们还能够让 task 超时,当超时的时候,执行 task 的线程将会被中断,并且 task 将会被标记为 failed。

如果咱们想继续执行,那么能够应用 –continue。

留神,只有可能响应中断的 task,timeout 才有用。

task hangingTask() {
    doLast {Thread.sleep(100000)
    }
    timeout = Duration.ofMillis(500)
}

task rule

如果咱们想要给某些 task 定义一些规定,那么能够应用 tasks.addRule:

tasks.addRule("Pattern: ping<ID>") { String taskName ->
    if (taskName.startsWith("ping")) {task(taskName) {
            doLast {println "Pinging:" + (taskName - 'ping')
            }
        }
    }
}

上咱们定义了一个 rule,如果 taskName 是以 ping 结尾的话,那么将会输入对应的内容。

看下运行后果:

> gradle -q pingServer1
Pinging: Server1

我还能够将这些 rules 作为依赖项引入:

task groupPing {dependsOn pingServer1, pingServer2}

Finalizer tasks

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

task taskX {
    doLast {println 'taskX'}
}
task taskY {
    doLast {println 'taskY'}
}

taskX.finalizedBy taskY

> gradle -q taskX
taskX
taskY

finalize task 是肯定会被执行的,即便下面的 taskX 中抛出了异样。

总结

以上就是 gradle 中 task 的详解,心愿大家可能喜爱。

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

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

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

正文完
 0