深刻理解gradle中的taskjava
在以前的文章中,咱们讲到了如何使用gradle建立一个简单的task,以及task之间怎么依赖,甚至使用了程序来建立task。在本文中,咱们会更加深刻的去了解一下gradle中的task。多线程
定义一个task能够有不少种方式,好比下面的使用string做为task的名字:ide
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:gradle
task(hello) { doLast { println "hello" } } task(copy, type: Copy) { from(file('srcDir')) into(buildDir) }
上面咱们在建立task的时候,使用了tasks集合类来建立task。ui
实际上,tasks集合类是一个很是有用的工具类,咱们可使用它来作不少事情。this
直接在build文件中使用tasks,其实是引用了TaskContainer的一个实例对象。咱们还可使用 Project.getTasks()
来获取这个实例对象。线程
咱们看下TaskContainer的定义:code
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 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的排序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一些描述信息,这样咱们在执行gradle tasks的时候,就能够查看到:
task copy(type: Copy) { description 'Copies the resource directory to the target directory.' from 'resources' into 'target' include('**/*.txt', '**/*.xml', '**/*.properties') }
有时候咱们须要根据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定义一些规则,那么可使用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 }
和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/
最通俗的解读,最深入的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注个人公众号:「程序那些事」,懂技术,更懂你!