gradle 生命周期 和 Task

74 阅读6分钟

1. 项目的生命周期

Gradle 项目的生命周期分为三大阶段: Initialization -> Configuration -> Execution. 每个阶段都有自己的职责。

Initialization 阶段主要目的是初始化构建, 它又分为两个子过程,一个是执行 Init Script,另一个是执行 Setting Script。

init.gradle文件会在每个项目 build 之前被调用,用于做一些初始化的操作,它主要有如下作用:

配置内部的仓库信息(如公司的 maven 仓库信息);
配置一些全局属性;
配置用户名及密码信息(如公司仓库的用户名和密码信息)。

Setting Script则更重要, 它初始化了一次构建所参与的所有模块。 Configuration 阶段这个阶段开始加载项目中所有模块的 Build Script。所谓 “加载” 就是执行 build.gradle 中的语句, 根据脚本代码创建对应的 task, 最终根据所有 task 生成由 Task 组成的有向无环图(Directed Acyclic Graphs),如下:

image.png

Execution 阶段:这个阶段会根据上个阶段构建好的有向无环图,按着顺序执行 Task【Action 动作】。

2. settings 文件

settings 文件

1、作用:主要是在 项目初始化阶段 确定一下引入 哪些工程 需要加入到项目构建中,为构建 项目工程树 做准备。

2、工程树:gradle 中有工程树的概念,类似于 maven 中的 project 与 module。

3、内容:里面主要定义了当前 gradle 项目及子 project 的项目名称

4、位置:必须放在根工程目录下。

5、名字:为 settings.gradle 文件,不能发生变化

6、对应实例:与 org.gradle.api.initialization.Settings 实例是一一对应的关系。每个项目只有一个 settings 文件。

7、关注:作为开发者我们只需要关注该文件中的 include 方法即可。使用相对路径【 : 】引入子工程

8、一个子工程只有在 setting 文件中配置了才会被 gradle 识别,这样在构建的时候才会被包含进去。

案例如下所示:

//根工程项目名 
rootProject.name = 'root' 
//包含的子工程名称 
include 'subject01' 
include 'subject02' 
include 'subject03' 
//包含的子工程下的子工程名称 
include 'subject01:subproject011' 
include 'subject01:subproject012'

项目名称中 ":" 代表项目的分隔符, 类似路径中的 "/". 如果以 ":" 开头则表示相对于 root project 。然后 Gradle 会 为每个带有 build.gradle 脚本文件的工程构建一个与之对应的 Project 对象。

每个带有 build.gradle 脚本文件的工程构建一个与之对应的 Project 对象。

3、task

1、task 创建

task A { 
    println "root taskA" 
    doFirst(){ 
        println "root taskA doFirst" 
    }doLast(){
         println "root taskA doLast" 
    } 
}

在控制台运行 gradle A 输出如下:

image.png

2、任务的依赖

task A { 
    doLast { 
        println "TaskA.." 
    } 
}

task 'B'{ 
    doLast { 
        println "TaskB.." 
    } 
}
//参数方式依赖: dependsOn后面用冒号 
task 'C'(dependsOn: ['A','B']){ 
    doLast {
        println "TaskC.." 
    } 
}

在控制台运行 gradle C

image.png

3、task 类型

上边定义的 task 都是 DefaultTask 类型的,如果要完成某些具体的操作完全需要我们自己去编写 gradle 脚本,势必有 些麻烦,那有没有一些现成的任务类型可以使用呢?有的,Gradle 官网给出了一些现成的任务类型帮助我们快速完成想 要的任务,我们只需要在创建任务的时候,指定当前任务的类型即可,然后即可使用这种类型中的属性和 API 方法了。

image.png

4、自定义 Task 类型

def myTask=task MyDefinitionTask (type: CustomTask) 
myTask.doFirst(){ 
    println "task 执行之前 执行的 doFirst方法" 
}
myTask.doLast(){ 
    println "task 执行之后 执行的 doLast方法" 
}
class CustomTask extends DefaultTask { 
    //@TaskAction表示Task本身要执行的方法 
    @TaskAction 
    def doSelf(){ 
        println "Task 自身 在执行的in doSelf" 
    } 
}

测试:  gradle MyDefinitionTask

image.png

5、动态分配任务

gradle 的强大功能不仅仅用于定义任务的功能。例如,可以使用它在循环中注册同一类型的多个任务

一旦注册了任务,就可以通过 API 访问它们。例如,可以使用它在运行时动态地向任务添加依赖项。Ant 不允许这样 的事情发生。

4.times { counter -> 
    tasks.register("task$counter") { 
        doLast { 
            println "I'm task number $counter" 
        } 
    } 
}
tasks.named('task0') { dependsOn('task2', 'task3') }

构建 4 个任务,但是任务 0 必须依赖于任务 2 和 3,那么代表任务 2 和 3 需要在任务 0 之前优先加载。具体测试如下

image.png

6、任务的关闭与开启

每个任务都有一个 enabled 默认为的标志 true。将其设置为 false 阻止执行任何任务动作。禁用的任务将标记为“跳过”。

task disableMe { 
    doLast { 
        println 'This task is Executing...' 
    }
    enabled(true)//直接设置任务开启,默认值为true 
}
//disableMe.enabled = false //设置关闭任务

7、任务的超时

每个任务都有一个 timeout 可用于限制其执行时间的属性。当任务达到超时时,其任务执行线程将被中断。该任务将 被标记为失败。终结器任务仍将运行。如果 --continue 使用,其他任务可以在此之后继续运行。不响应中断的任务无法 超时。Gradle 的所有内置任务均会及时响应超时

task a() { 
    doLast { 
        Thread.sleep(1000) 
        println "当前任务a执行了" 
    }
    timeout = Duration.ofMillis(500) 
}
task b() { 
    doLast { 
        println "当前任务b执行了" 
    } 
}

在控制台使用: gradle a b 测试会发现执行 a 的时候,由于 a 执行超时,抛出异常,所以没有继续往下执行【b 也没执行】。 然后在控制台使用: gradle a b –continue,测试会发现 a 虽然执行失败,但是 b 还是执行了。

image.png

8、任务的查找

task abc { 
    doLast { 
        println "永远的hello world" 
        } 
    }
    //根据任务名查找 
    tasks.findByName("abc").doFirst({println "北京...."}) 
    tasks.getByName("abc").doFirst({println "深圳...."}) 
    //根据任务路径查找【相对路径】 
    tasks.findByPath(":abc").doFirst({println "上海...."}) 
    tasks.getByPath(":abc").doFirst({println "武汉...."})

9、任务规则

当我们执行、依赖一个不存在的任务时,Gradle 会执行失败,报错误信息。那我们能否对其进行改进,当执行一个不存在 的任务时,不是报错而是打印提示信息呢?

task hello { 
    doLast { 
        println 'hello world' 
    } 
}

tasks.addRule("对该规则的一个描述,便于调试、查看等"){ 
    String taskName -> task(taskName) { 
        doLast { 
            println "该${taskName}任务不存在,请查证后再执行" 
        } 
    } 
}

测试: 使用 gradle abc hello 进行测试,此时当 abc 任务不存在时,也不会报异常【不中断执行】而是提示自定义的规则信息,继续执行 hello 任务。此外,它还可以 根据不同的规则动态创建需要的任务 等情况。

10、任务的 onlyIf 断言

断言就是一个条件表达式。Task 有一个 onlyIf 方法。它接受一个闭包作为参数,如果该闭包返回 true 则该任务执行, 否则跳过 。这有很多用途,比如控制程序哪些情况下打什么包,什么时候执行单元测试,什么情况下执行单元测试的时 候不执行网络测试等。具体案例如下所示:

task hello { doLast { println 'hello world' } } hello.onlyIf { !project.hasProperty('fensi') }

测试:通过-P 为 Project 添加 fensi 属性

gradle hello -Pfensi

11、 默认任务

Gradle 允许您定义一个或多个在没有指定其他任务时执行的默认任务。
代码如下所示:

defaultTasks 'myClean', 'myRun' 
tasks.register('myClean'){ 
    doLast { 
        println 'Default Cleaning!' 
    } 
}
tasks.register('myRun') { 
    doLast { 
        println 'Default Running!' 
    } 
}
tasks.register('other') { 
    doLast { 
        println "I'm not a default task!" 
    } 
}

测试及结果如下:

gradle -q  
Default Cleaning!

Gradle 允许您定义一个或多个在没有指定其他任务时执行的默认任务。
代码如下所示:

defaultTasks 'myClean', 'myRun' 
tasks.register('myClean'){ 
    doLast { 
        println 'Default Cleaning!' 
    } 
}
tasks.register('myRun') { 
    doLast { 
        println 'Default Running!' 
    } 
}
tasks.register('other') { 
    doLast { 
        println "I'm not a default task!" 
    } 
}

测试及结果如下:

gradle -q  
Default Cleaning!  
Default Running!