Android Gradle学习(二):如何创建Task 基于Gradle7 更新

文章写得很好, 可惜Gradle更新太快。  导致打开老项目时经常出错

 

一个 Task 是 Gradle 里项目构建的原子执行单元,Gradle 通过将一个个Task串联起来完成具体的构建任务,每个 Task 都属于一个 Project。关于 Task 的具体定义可查看官方文档Gradle Task API

1. 在Gradle里定义Task

在 build.gradle 里可以通过 task 关键字来创建Task:

task myTask
task myTask { configure closure }
task myTask(type: SomeType)
task myTask(type: SomeType) { configure closure }

我们来试验一下,新建一个 build.gradle 文件,在里面创建2个最简单的task:

task myTask1 {
    println "configure task1"
}

task myTask2 {
    println "configure task2"
}

执行其中一个task:gradle myTask1

> Configure project :
configure task1
configure task2

上面定义了2个 task :myTask1、myTask2,但是当我们执行 myTask1 时,发现2个 task 括号内部的代码都被执行了。括号内部的代码我们称之为配置代码,在 gradle 脚本的配置阶段都会执行,也就是说不管执行脚本里的哪个任务,所有 task 里的配置代码都会执行。这似乎与我们的期望不一致,通常我们写程序时调用一个方法,这个方法里的代码才会执行,那么我们执行一个 Task 时,这个 Task 里的代码才会被执行才对啊。显然 Gradle 里是不一样的,这个问题就涉及到 Task Action 的概念了。

2. Task Actions

一个 Task 是由一序列 Action (动作)组成的,当运行一个 Task 的时候,这个 Task 里的 Action 序列会按顺序依次执行。前面例子括号里的代码只是配置代码,它们并不是 Action ,Task 里的 Action 只会在该 Task 真正运行时执行,Gralde 里通过 doFirst、doLast 来为 Task 增加 Action 。

  • doFirst:task执行时最先执行的操作
  • doLast:task执行时最后执行的操作
task myTask1 {
    println "configure task1"
}

task myTask2 {
    println "configure task2"
}

myTask1.doFirst {
    println "task1 doFirst"
}

myTask1.doLast {
    println "task1 doLast"
}

myTask2.doLast {
    println "task2 doLast
}

同样执行myTask1:gradle myTask1,这次的结果如下:

> Configure project :
configure task1
configure task2

> Task :myTask1
task1 doFirst
task1 doLast

从上面例子中可以看到,所有 Task 的配置代码都会运行,而 Task Actions 则只有该 Task 运行时才会执行。

3. doLast等价操作

doLast有一种等价操作叫做leftShift,leftShift可以缩写为 << ,下面几种写法效果是一模一样的:

myTask1.doLast {
    println "task1 doLast"
}

myTask1 << {
    println "task1 doLast<<"
}

myTask1.leftShift {
    println "task1 doLast leftShift"
}

需要注意的是 << 操作符,它只是一种 Gradle 里的语法糖而已,不要被这种写法迷惑了。

4. 创建Task的几种常见写法

task myTask1 {
    doLast {
        println "doLast in task1"
    }
}

task myTask2 << {
    println "doLast in task2"
}

//采用 Project.task(String name) 方法来创建
project.task("myTask3").doLast {
    println "doLast in task3"
}

//采用 TaskContainer.create(String name) 方法来创建
project.tasks.create("myTask4").doLast {
    println "doLast in task4"
}

project.tasks.create("myTask5") << {
    println "doLast in task5"
}

初次接触这些写法,头都是大的,Gradle太灵活了,个人觉得记住最常用的即可,看到类似写法能看懂就行了。

5. 创建Task的参数介绍

在 Gradle 中定义 Task 的时候,可以指定更多的参数,如下所示:

参数名 含义 默认值
name task的名字 必须指定,不能为空
type task的父类 默认值为org.gradle.api.DefaultTask
overwrite 是否替换已经存在的同名task false
group task所属的分组名 null
description task的描述 null
dependsOn task依赖的task集合
constructorArgs 构造函数参数

我们来测试下:

task myTask1 << {
    println "doLast in task1"
}

 << 从 Gradle5 不支持,  用doLast 替换

task myTask2 << { println "doLast in task2" } task myTask3 << { println "doLast in task3, this is old task" } task myTask3(description: "这是task3的描述", group: "myTaskGroup", dependsOn: [myTask1, myTask2], overwrite: true) << { println "doLast in task3, this is new task" }

执行 gradle myTask3,结果如下:

> Task :myTask1
doLast in task1

> Task :myTask2
doLast in task2

> Task :myTask3
doLast in task3, this is new task

执行命令 gradle -q tasks --all,查看下 task 信息,节选我们创建的 task 信息如下:

MyTaskGroup tasks
------------
myTask3 - 这是task3的描述

Other tasks
-----------
myTask1
myTask2

上面例子中创建了2个名为 myTask3 的 task,但是后一个将前一个替换掉了,在分组信息里多了个一个名为 MyTaskGroup 的分组,其他没有命名分组的统一归到 Other 这个分组里去了。

我们再来看看 type 参数怎么个用法,在 Gradle 中通过 task 关键字创建的 task,默认的父类都是 org.gradle.api.DefaultTask,这里定义了一些 task 的默认行为。看看下面这个例子:

//自定义Task类,必须继承自DefaultTask
class SayHelloTask extends DefaultTask {
    
    String msg = "default name"
    int age = 18        

    //构造函数必须用@javax.inject.Inject注解标识
    @javax.inject.Inject
    SayHelloTask(int age) {
        this.age = age
    }

    //通过@TaskAction注解来标识该Task要执行的动作
    @TaskAction
    void sayHello() {
        println "Hello $msg ! age is ${age}"
    }

}

//通过constructorArgs参数来指定构造函数的参数值
task hello1(type: SayHelloTask, constructorArgs: [30])

//通过type参数指定task的父类,可以在配置代码里修改父类的属性
task hello2(type: SayHelloTask, constructorArgs: [18]) {
        //配置代码里修改 SayHelloTask 里的字段 msg 的值
    msg = "hjy"
}

报错

Reason: A property without annotation isn't considered during up-to-date checking.

Possible solutions:
1. Add an input or output annotation.
2. Mark it as @Internal.


修改
//自定义Task类,必须继承自DefaultTask
class SayHelloTask extends DefaultTask {
@Input String msg = "default name"

@Input int age = 18 //构造函数必须用@javax.inject.Inject注解标识 @javax.inject.Inject SayHelloTask(int age) { this.age = age } //通过@TaskAction注解来标识该Task要执行的动作 @TaskAction void sayHello() { println "Hello $msg ! age is ${age}" } } //通过constructorArgs参数来指定构造函数的参数值 task hello1(type: SayHelloTask, constructorArgs: [30]) //通过type参数指定task的父类,可以在配置代码里修改父类的属性 task hello2(type: SayHelloTask, constructorArgs: [18]) { //配置代码里修改 SayHelloTask 里的字段 msg 的值 msg = "hjy" }

  


执行这2个 task 查看运行结果如下:

> Task :hello1
Hello default name ! age is 30

> Task :hello2
Hello hjy ! age is 18

上面这个例子中,演示了如何自己创建一个 Task 类,这里只是一些最简单的用法,每个 Task 应该还有输入、输出等等,且待下章分解。



作者:云飞扬1
链接:https://www.jianshu.com/p/8e89a0b8acf8
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
上一篇:jsonobject


下一篇:【Java基础】检查型非检查型异常、try-catch、try-with-resources、throws、finally、getMessage、printStackTrace、自定义异常、实现断言类