> 文档中心 > Android Gradle(八)Task基础和自定义Task

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,那么就会按照代码顺序执行,先执行learnEnglishgetBook。加了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,