Android Gradle学习(三) Task进阶学习

前面通过很多范例讲了在 build.gradle 中怎么创建 Task,但是 Task 到底是个什么东西,它里面有些什么,我们并不清楚,本文试图揭开 Task 神秘的面纱。

1. Task类图

Gradle 所说的 Task 是 org.gradle.api.Task 接口,默认实现是 org.gradle.api.DefaultTask 类,其类图大概如下(只节选了比较重要的部分):
在这里插入图片描述
我们只需关注 Task 接口即可,从这里基本可以了解到 Task 有哪些特性。

2. Task接口解析

为了了解每个接口方法的含义,我们直接用代码来测试验证下:

class SayHelloTask extends DefaultTask {

    String msg = "default name";
    int age = 20

    @TaskAction
    void sayHello() {
        println "Hello $msg ! Age is ${age}"
    }

}

task test1 << {
    println "task test1 exec..."
}
task test2 << {
    println "task test2 exec..."
}
task test3 << {
    println "task test3 exec..."
}
task hello(type: SayHelloTask, group: "MyGroup")

//对task进行配置,
hello.configure {
    println "hello task configure"
    msg = "hjy"
}

//获取task的名称
println "task name is ${hello.getName()}"
//获取task的组名
println "task group is ${hello.getGroup()}"

//设置task里的属性值,设置 age = 70
hello.setProperty("age", 70)
//获取task里的某个属性值
println "task msg is ${hello.property('msg')}"

//设置依赖的task,只有test1 task执行完后才会执行hello task
hello.dependsOn(test1)
//设置终结者任务,执行完hello task之后会执行test2 task,通常可以用该方法做一些清理操作
hello.finalizedBy(test2)

//如果同时执行hello、test3这2个task,会确保test3执行完之后才执行hello这个task,用这个来保证执行顺序
hello.setMustRunAfter([test3])

//设置满足某个条件后才执行该task
hello.setOnlyIf {
    //只有当 age = 70 时,才会执行task,否则不会执行
    return hello.property("age") == 70
}

我们执行任务gradle hello test3,结果如下:

> Configure project :
hello task configure
task name is hello
task group is MyGroup
task msg is hjy

> Task :test3
task test3 exec...

> Task :test1
task test1 exec...

> Task :hello
Hello hjy ! Age is 70

> Task :test2
task test2 exec...

3. TaskContainer接口解析

TaskContianer 是用来管理所有的 Task 实例集合的,可以通过 Project.getTasks() 来获取 TaskContainer 实例。

org.gradle.api.tasks.TaskContainer接口:
//查找task
findByPath(path: String): Task
getByPath(path: String): Task
getByName(name: String): Task
withType(type: Class): TaskCollection
matching(condition: Closure): TaskCollection

//创建task
create(name: String): Task
create(name: String, configure: Closure): Task 
create(name: String, type: Class): Task
create(options: Map<String, ?>): Task
create(options: Map<String, ?>, configure: Closure): Task

//当task被加入到TaskContainer时的监听
whenTaskAdded(action: Closure)

我们先来看看创建 task 的方法:

//当有task创建时
getTasks().whenTaskAdded { Task task ->
    println "The task ${task.getName()} is added to the TaskContainer"
}

//采用create(name: String)创建
getTasks().create("task1")

//采用create(options: Map<String, ?>)创建
getTasks().create([name: "task2", group: "MyGroup", description: "这是task2描述", dependsOn: ["task1"]])

//采用create(options: Map<String, ?>, configure: Closure)创建
getTasks().create("task3", {
    group "MyGroup"
    setDependsOn(["task1", "task2"])
    setDescription "这是task3描述"
})

执行命令gradle -q tasks --all,查看是否创建成功,结果如下:

MyGroup tasks
-------------
task2 - 这是task2描述
task3 - 这是task3描述

Other tasks
-----------
task1

我们再来试试查找 task 的方法:

//通过名字查找指定的task
def task3 = getTasks().findByName("task3")
println "findByName() return task is " + task3

def taskList = getTasks().withType(DefaultTask)
def count = 0
//遍历所有的task,打印出其名字
taskList.all { Task t ->
    println "${count++} task name is ${t.name}"
}

4. Task增量构建

Gradle 支持一种叫做 up-to-date 检查的功能,也就是常说的增量构建。Gradle 的 Task 会把每次运行的结果缓存下来,当下次运行时,会检查输出结果有没有变更,如果没有变更则跳过运行,这样可以提高 Gradle 的构建速度。

通常,一个 task 会有一些输入(inputs)和一些输出(outputs),task 的输入会影响其输出结果,以官网中的一张图为例:
在这里插入图片描述

图中表示一个 java 编译的 task,它的输入有2种,一是 JDK 版本号,一是源文件,它的输出结果为 class 文件,只要 JDK 版本号与源文件有任何变动,最终编译出的 class 文件肯定是不同的。当我们执行过一次编译任务后,再次运行该 task ,如果发现它的输入没有任何改变,那么它编译后的结果肯定也是不会变化的,可以直接从缓存里获取输出,这样 Gradle 会标识该 task 为 UP-TO-DATE,进而跳过该 task 的执行。

4.1 TaskInputs、TaskOutputs

那么怎么实现一个增量构建呢?一个增量构建必须至少指定一个输入、一个输出,从前面 Task 的类图中可以看到,Task.getInputs() 对象类型为 TaskInputs,Task.getOutputs() 对象类型为 TaskOuputs,从中也可以看到inputs、outputs都支持哪些数据类型。同样以一个简单例子来说明:

task test1 {
    //设置inputs
    inputs.property("name", "hjy")
    inputs.property("age", 20)
    //设置outputs
    outputs.file("$buildDir/test.txt")

    doLast {
        println "exec task task1"
    }
}

task test2 {
    doLast {
        println "exec task task2"
    }
}

连续2次运行task,执行命令gradle test1 test2,结果如下:

//第一次的运行结果
> Task :test1
exec task task1

> Task :test2
exec task task2

BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed

//第二次的运行结果
> Task :test2
exec task task2

BUILD SUCCESSFUL in 0s
2 actionable tasks: 1 executed, 1 up-to-date

从结果中可以看到,第2次运行时,test1 task 并没有运行,而是被标记为 up-to-date,而 test2 task 则每次都会运行,这就是典型的增量构建。

4.2 taskInputs、taskOutputs注解

当你自定义 task class 时,可以通过注解来实现增量构建,这是一种更加灵活方便的方式。我们常用的注解包括:

注解名属性类型描述
@Input任意Serializable类型一个简单的输入值
@InputFileFile一个输入文件,不是目录
@InputDirectoryFile一个输入目录,不是文件
@InputFilesIterableFile列表,包含文件和目录
@OutputFileFile一个输出文件,不是目录
@OutputDirectoryFile一个输出目录,不是文件
@OutputFilesMap<String, File>或Iterable输出文件列表
@OutputDirectoriesMap<String, File>或Iterable输出目录列表

我们通过自定义一个 task class 来演示一下用法:

class SayHelloTask extends DefaultTask {

    //定义输入
    @Input
    String username;
    @Input
    int age

    //定义输出
    @OutputDirectory
    File destDir;

    @TaskAction
    void sayHello() {
        println "Hello $username ! age is $age"
    }

}

task test(type: SayHelloTask) {
    age = 18
    username = "hjy"
    destDir = file("$buildDir/test")
}

执行该 task 之后,会自动生成一个 $buildDir/test 文件目录,当你再次运行时就实施增量构建。但是当你修改 age、username 的值,或者删除磁盘上 $buildDir/test 目录,再次运行该 task ,该 task 就会重新运行。

相关文章
Android Gradle学习(一) Gradle基础入门
Android Gradle学习(二) 如何创建Task
Android Gradle学习(三) Task进阶学习
Android Gradle学习(四) Project详解
Android Gradle学习(五) Extension详解
Android Gradle学习(六) NamedDomainObjectContainer详解
Android Gradle学习(七) Gradle构建生命周期
Android Gradle学习(八) 统计Task执行时长

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值