Android Gradle(八)Task基础和自定义Task
一、前言
在Gradle中,学习和使用Task是我们必不可少的内容。什么是Task?Task就是一个操作,你可以用来移动复制文件、自定义输出打包apk文件名或者上传一些文件或者jar到Maven仓库等。再简单地说,可以理解成一个Java方法。Android中最常见的Task,如下:
task clean(type: Delete) { delete rootProject.buildDir}
可以看到,他的任务就是删除根目录下的buildDir文件夹 。接下来,让我们看看如何创建一个任务😀
二、创建一个任务
PS:以下的代码都是放在build.gradle中运行,如果想在单独的gradle文件运行,必须apply进项目里才行,例如 apply from: 'custom.gradle'。
在Gradle中,创建一个任务的方式很简单,如下:
task printfHello{ println "printfHello 配置阶段" doFirst { println "Hello doFirst" } doLast { println "Hello doLast" }}
如上所示,一个简单的任务就创建成功了,其中doFirst,doLast分别代码在任务的执行前后对任务的操作。
上面的代码等同于如下代码:
task("printfHello"){ println "printfHello 配置阶段" doFirst { println "Hello doFirst" } doLast { println "Hello doLast" }}//或者def Task customTask = task("customTask")customTask.doFirst { println "customTask start"}
如何运行我们的任务呢?
1.点击代码旁边的绿色标志
2.在Terminal下输入如下指令:
gradlew printfHello
效果如下:
可以看到,分别输出了配置阶段的任务代码和运行阶段的任务代码。从此也能看出,一开始clean Task就在配置阶段进行文件夹的删除。
关于Gradle的生命周期可以看这里,其实也就三个阶段,初始化阶段 》》配置阶段 》》运行阶段
接下来我们可以看下task对应的源码,如下:
Task task(String name, Closure configureClosure);
其实task就是Project对象的一个方法,第一个参数是任务名,第二个参数是一个闭包,这个闭包的作用就是对我们创建的任务进行相关的配置和执行。
除了上面的方法,还可以通过TaskContaniner集合来创建。代码如下:
tasks.create("printHelloWorld"){ doFirst { println "Hello World doFirst" } doLast { println "Hello World doLast" }}
这两种创建任务的方式作用都是一样的,只是创建的方式不一样。一般使用第一种,会比较简洁。
三、任务依赖
每个任务之间是有依赖的,可以方便我们控制任务执行的顺序。例如一个Apk的生成,肯定需要先把Java转成Class,再把Class转成Dex文件,然后再把Dex文件与其他资源文件合成一个Apk文件。下面让我们看看任务间是如何依赖的,如下:
//准备内存task buildMemory{ doFirst { println "buildMemory start" } doLast { println "buildMemory finish" }}//准备CPUtask buildCpu{ doFirst { println "buildCpu start" } doLast { println "buildCpu finish" }}//组装电脑task assembleComputer{ dependsOn(buildCpu,buildMemory) doFirst { println "assembleComputer start" } doLast { println "assembleComputer finish" }}
如上面的代码,依赖另外一个task的关键方法是dependsOn,参数是依赖的task任务,也就是先执行的任务。运行效果如下:
> Task :buildCpubuildCpu startbuildCpu finish> Task :buildMemorybuildMemory startbuildMemory finish> Task :assembleComputerassembleComputer startassembleComputer finish
依赖任务的方式还有如下写法:
task assembleComputer(dependsOn: [buildCpu,buildMemory])
四、自定义继承Task
不知道你会不会有疑惑,task有doFirst,doLast方法,那任务执行中的方法在哪里?如果想知道答案的话,继续往下看。先上一段代码,如下:
def customTask = task customtask(type:CustomTask)customTask.doFirst { println "CustomTask doFirst"}customTask.doLast { println "CustomTask doLast"}abstract class CustomTask extends DefaultTask { @Override Task doFirst(Action action) { return super.doFirst(action) } @TaskAction def doSelf(){ println "CustomTask doSelf" }}
先解释一下几个关键:
- type是一个关键字,用来引入一个存在的Task,例如我们创建的CustomTask
- DefaultTask,用来提供给用户自定义Task的基类
- @TaskAction是一个注解标注,意思是被标注的方法就是Task要执行的方法,可以多个。
执行代码效果图如下:
> Task :customtaskCustomTask doFirstCustomTask doSelfCustomTask doLast
可以看到,任务的执行前、执行中、执行后的逻辑都出来了。
那他是如何做到这样顺序执行的呢?
原理很简单,DefaultTask维护了一个actions的List,那只要我们按顺序添加doFirst、doSelf(把自己执行的代码叫doSelf)、doLast,然后便利执行actions List,那不久可以做到如上面的效果。
doFirst添加进actions List(往0下标下标插入)源码如下:
public Task doFirst(final Closure action) { taskMutator.mutate("Task.doFirst(Closure)", new Runnable() { @Override public void run() { getTaskActions().add(0, convertClosureToAction(action, "doFirst {} action")); } }); }
doLast添加进actions List(往最后插入)源码如下:
public Task doLast(final Closure action) { taskMutator.mutate("Task.doLast(Closure)", new Runnable() { @Override public void run() { getTaskActions().add(convertClosureToAction(action, "doLast {} action")); } }); return this; }
可以看到,doFirst下标的位置肯定是在doLast之前的,再来看看doSelf是如何执行的。
doSelf添加进actions List(往0下标下标插入)源码如下:
public void prependParallelSafeAction(final Action action) { if (action == null) { throw new InvalidUserDataException("Action must not be null!"); } getTaskActions().add(0, wrap(action)); }
看到这里,你会不会疑惑?也是往0下标插入,那如何保证顺序呢?
答案是肯定可以保证的,因为这个方法的调用是在解析@TaskAction标注的时候执行的,也就是此方法被执行时actions List里面的元素是空的。
五、其他功能补充
5.1 强制排序
在你同时执行多个任务的时候,可能需要任务B必须在任务A执行完才能执行。代码如下:
taskB.mustRunAfter(taskA) //表示taskB必须在taskA后执行taskB.shouldRunAfter(taskA) //表示taskB应该在taskA后执行,这个相比较mustRunAfter,可能会出现意外的情况
示例如下:
task getBook{ doLast { println "getBook finish" }}task learnEnglish{ doFirst { println "learnEnglish start" } doLast { println "learnEnglish finish" }}learnEnglish.mustRunAfter(getBook) //表示learnEnglish任务必须在getBook任务后执行
执行如下指令:
gradlew learnEnglish getBook
如果没有加mustRunAfter,那么就会按照代码顺序执行,先执行learnEnglish再getBook。加了mustRunAfter之后,执行效果如下:
5.2 启用/禁止任务
Task中有一个enable属性,表示启用或禁止任务,默认是true,表示启用;false表示禁止任务执行,执行时会被跳过。修改上面的getBook代码,增加enable为禁止,如下:
task getBook{ enabled(false)}
执行代码,getBook任务没有被执行到,效果图如下:
5.3 任务的onlyIf
Task中有一个onlyIf的方法,如果返回true,说明任务可以执行,否则就跳过。我们继续给getBook增加一个onlyIf方法,用来判断获取的书籍是不是英语书,如下代码:
getBook.onlyIf{ def book = "世界百科" if (book =="英语书"){ return true }else { false }}
执行代码,getBook任务没有被执行到,效果图如下:
PS:这个onlyIf方法可以使用在任何有Task的场景,不要局限在mustRunAfter,mustRunAfter只是控制Task的执行顺序。
5.4 任务的分组和描述
在Task中,我们可以给Task分组和添加描述,可以让开发者更加直观的知道这个Task的作用。代码如下:
task learnEnglish{ group(BasePlugin.BUILD_GROUP) description("这个一个learnEnglish Task")}
- group:给task分组,可以显示在对应的分组里,例如上面BUILD_GROUP,显示在build分组,其他的选项也是如此效果。
- description:给task添加一个描述,可以通过gradlew tasks命令查看
Sync Now代码,在build中多了一个learnEnglish Task,