一、前言

    Gradle的核心是一种基于依赖的编程语言。在Gradle语法中,你可以定义任务和任务之间的依赖关系,Gradle保证这些任务按照依赖的顺序执行,并且每个任务只执行一次。这些任务形成一个有向无环图,一些构建工具可以在执行这些任务时构建这样的依赖关系图,Gradle在执行任何任务之前就已经构建出完整的依赖关系图。这就是Gradle的核心,让原本不可能的事情称为可能。

在Gradle中通过构建脚本来配置依赖关系图,因此严格来说他们是构建配置脚本

二、Gradle构建的阶段

    Gradle构建主要可以分为以下三个阶段:

  • 初始化阶段(Initialization)
        Gradle支持单项目构建和多项目构建。在初始化阶段,Gradle将决定哪些项目将会加入到构建中,并且为加入构建中的每一个项目创建一个 Project 实例对象。
  • 配置阶段(Configuration)
        在配置阶段,项目的 Project 实例对象将会进行配置。所有加入构建的项目的构建脚本,作为整个构建的一部分将会执行。
  • 执行阶段(Execution)
        Gradle在配置阶段创建和配置好任务子集,任务子集由传入Gradle命令的任务名称参数以及当前目录决定,在执行阶段将会控制这些任务子集中的任务按顺序逐个执行。

三、关于构建的设置文件(settings.gradle)

    除了构建脚本文件之外,Gradle还定义了一个设置文件。设置文件由Gradle根据命名惯例进行命名,默认文件名为 settings.gradle。在后面,将会介绍Gradle是如何查找设置文件。

    设置文件在初始化阶段中会被执行。一个多项目的构建必须在项目体系中的根项目中包含一个设置文件,之所以必须是因为设置文件中定义了那些项目将会作为添加到多项目构建中。对于单项目构建,设置文件是可选的。设置文件除了定义所包含的子项目之外,还可以定义向构建脚本类路径中添加库。设置文件的属性访问和方法调用委托给设置实例对象(也就是说在脚本配置文件或者设置文件中,可以通过设置实例对象访问属性和调用方法),关于设置实例对象相关的属性和方法定义,可以参考Gradle API文档中关于 Settings 的详细介绍。

说明:设置文件中包含各种在初始化之前就配置好的构建配置,例如项目体系根目录、构建缓存、多项目构建包含的子项目等等,可以通过此配置文件变更默认配置,有兴趣的同学可以详细阅读Gradle API文档中关于 Settings 的介绍。

四、构建的初始化阶段详解

    前面提到,在Gradle构建的初始化阶段,需要先决定哪些项目会加入到构建中,而且也提到Gradle构建分为单项目构建和多项目构建两种,那么Gradle是如何知道构建是单项目还是多项目?如果你在包含 settings.gradle 文件的目录中发起多项目构建,Gradle 会根据 settings.gradle 文件来配置构建。Gradle同样允许你在多项目构建内的任意子项目内部执行构建,如果在没有 settings.gradle 配置文件的子项目中执行Gradle构建,Gradle将会通过以下途径查找 settings.gradle 文件:


Created with Raphaël 2.3.0 开始Gradle构建 当前目录不存在 settings.gradle 文件 在父目录中查找 settings.gradle 文件 父目录存在 settings.gradle 文件? 当前项目是否 settings.gradle 中的子项目 作为多项目构建执行 结束查找(继续构建项目内容) 作为单项目构建执行 yes no yes no


    从前面的描述中,我们了解到查找 settings.gradle 的目的,就是为了让Gradle确定执行构建所在的项目是否为多项目构建的一个子项目。当然,如果执行构建所在的目录是多项构建的一个子项目,也只有与之相互依赖的项目会执行构建(其他无关的项目并不会构建),但是Gradle还是需要为整个多项目构建创建构建配置。如果当前项目中存在 settings.gradle 文件,构建流程会遵循以下原则:

  • 作为单项目构建: 如果 settings.gradle 文件中没有定义多项目体系。
  • 作为多项目构建: 如果 settings.gradle 文件中定义了多项目体系。

    这种自动搜索 settings.gradle 文件的机制仅适用于具有默认项目布局的多项目构建,其中的项目路径与物理磁盘中的子项目布局需要相匹配。Gradle支持任意物理布局的多项目构建,但是针对任意布局的项目构建,你必须在包含 settings.gradle 文件的目录中执行构建。

    Gradle为参与构建的每一个子项目都创建一个 Project 实例。对于多项目构建,这些 Project 实例是 Settings 实例中的属性(Settings 实例是根据项目根目录中的 settings.gradle 设置文件创建)。每一个 Project 实例有着一个和项目顶级目录名同名的默认名称。除根项目(根目录所在的项目)之外的子项目都有一个父项目,所有的项目都可能有子项目。

五、单项目构建的配置和执行

    对于单项目构建,初始化阶段之后的流程就非常简单了,构建脚本会针对初始化阶段创建的 Project 对象执行。Gradle会查找那些跟命令行传入的参数相同名称的任务,如果这些任务名称存在,它们就会根据你传入参数的顺序,作为单独的构建执行任务。

六、 针对构建脚本的生命周期做出相应

    您的构建脚本可以在构建过程经过生命周期的时候接收到通知,这些通知通常有两种方法捕获:一种是通过实现指定的监听接口;另一种是在通知发送之后提供一个闭包(closure)并执行。

说明:以下的示例都是通过闭包的方式捕获通知,如果需要了解如何通过监听接口捕获,可以参考官方相关的 API 文档。

6.1 项目评估通知

    在一个项目评估前和评估完成后,您都可以立即接收到通知。您可以在接收到通知时做一些操作,比如在构建脚本中所有的定义均已生效应用的情况下增加一些额外的配置,或者自定义日志输出和分析。我们可以看一下下面的示例:

  • 根项目中的 build.gradle
allprojects {
    // ...

    beforeEvaluate {
        println("Project ${project.name} -- beforeEvaluate")
    }

    afterEvaluate {Project project ->
        println("Project ${project.name} -- afterEvaluate")
    }
    // ...
}

// 为了方便测试,在build.gradle文件中添加一个任务(笔者用AndroidStudio的Android项目做测试,所以添加一个简单的任务可以避免执行build任务的长时间等待)
tasks.register("testG") {
    println("regist task testG")
}
  • 控制台输出
E:\AndroidStudioProjects\ElectricityNoteLocal>gradlew -q testG // 我们使用项目中的gradlew命令,可以免去各种环境配置
This is execute in initialization ----- settings.gradle
Project ElectricityNoteLocal -- afterEvaluate
Project app -- beforeEvaluate
Project app -- afterEvaluate
regist task testG

讲解:在以上示例中,测试的项目是多项目构建(根项目ElectricityNoteLocal和一个子项目app),我们在根项目采用了闭包的方式对所有项目添加了评估监听,根据输出我们可以看到,在任务执行之前,必须要完成所有项目的评估。另外,可以看到无法接收到添加监听的 build.gradle 文件所在的项目的 beforeEvaluate 通知,这个需要特别注意,如果需要添加配置,尽量考虑使用 afterEvaluate

    在任何项目评估完成之后,也可以接收到通知。通过 Project.afterEvaluate() 添加闭包来接收评估完成之后的通知。需要注意的是,无论项目评估成功还是失败(出现异常),都可以接收到 afterProject 通知,因此可以在接收到通知的时候,判断评估结果。

  • 根项目中的 build.gradle
gradle.afterProject {Project project ->
    if(project.state.failure) {
        println("afterProject ---- ${project.name} -- failure")
    } else {
        println("afterProject ---- ${project.name} -- successed")
    }
}

// 为了方便测试,在build.gradle文件中添加一个任务(笔者用AndroidStudio的Android项目做测试,所以添加一个简单的任务可以避免执行build任务的长时间等待)
tasks.register("testG") {
    println("regist task testG")
}
  • 控制台输出
E:\AndroidStudioProjects\ElectricityNoteLocal>gradlew -q testG
This is execute in initialization ----- settings.gradle
afterProject ---- ElectricityNoteLocal -- successed
Project ElectricityNoteLocal -- afterEvaluate
afterProject ---- app -- successed
Project app -- afterEvaluate
regist task testG

注意事项:
1. Gradle.afterProject() 添加闭包是针对 Gradle 级别的全局监听,在 build.gradle 最外层添加即可,如果在 allprojects {} 里添加,相当于每个项目都添加了一次,执行时会收到多次通知(每次添加都会收到一次通知);
2. Project.afterEvaluate() 通知会在 Gradle.afterProject() 之后;3. Project.afterEvaluate() 通知同样可以在闭包中添加 Project 参数获取项目评估状态。

你也可以在 Gradle 层级通过 Gradle.addProjectEvaluationListener() 添加项目评估监听。

6.2 任务创建通知

    当一个任务添加到项目中的时候会立即收到通知。可以在收到任务添加通知中设置默认值,或者在任务添加到 build.gradle之前添加一些行为动作。

  • 根项目中的 build.gradle
tasks.whenTaskAdded {Task task ->
    println("Task ${task.name} is added")
}

// 为了方便测试,在build.gradle文件中添加一个任务(笔者用AndroidStudio的Android项目做测试,所以添加一个简单的任务可以避免执行build任务的长时间等待)
tasks.register("testG") {
    println("regist task testG")
}
  • 控制台输出
E:\AndroidStudioProjects\ElectricityNoteLocal>gradlew -q testG
This is execute in initialization ----- settings.gradle
Task testG is added
regist task testG

注意事项:由于 build.gradle 是脚本文件,所以 tasks.whenTaskAdded 只能监听到在他定义后面添加的任务。

6.3 任务执行图谱就绪通知

    在任务执行图谱填充完成时会立即接收到通知。可以通过向 TaskExecutionGraph 中添加一个 TaskExecutionGraphListener 监听来接收通知。看看下面的示例:

  • 根项目中的 build.gradle
gradle.taskGraph.addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
    @Override
    void graphPopulated(TaskExecutionGraph taskExecutionGraph) {
        println("TaskGraph populated ${taskExecutionGraph.allTasks}")
    }
})
tasks.register("testG") {
    println("regist task testG")
    it.doLast {
        println("TaskG do Last")
    }
}

task testA() {
    println("define task testA")
    dependsOn("testG")
    doLast {
        println("Task testA do Last")
    }
}
  • 控制台输出
E:\AndroidStudioProjects\ElectricityNoteLocal>gradlew -q testA
define task testA
regist task testG
TaskGraph populated [task ':testG', task ':testA']
TaskG do Last
Task testA do Last

说明:任务执行图谱就绪通知是在一切任务执行之前发出的,从上面的示例也可以看到,完成任务的创建(注册)之后,任务执行图谱执行完毕之后,就会收到任务图谱就绪通知,通知发出之后才会执行任务。

6.4 任务执行通知

    在任务执行前和执行后,都可以立即收到通知。通过向 Gradle 添加 beforeTaskafterTask 闭包可实现接收任务执行前和任务执行后通知,如下示例所示:

  • 根项目中的 build.gradle
gradle.taskGraph.beforeTask { Task task ->
    println("BeforTask --- ${task.name}")
}

gradle.taskGraph.afterTask { Task task, TaskState taskState ->
    println("AfterTask --- ${task.name} --- ${task.state == taskState.failure ? 'failure' : 'success'}")
}

tasks.register("testG") {
    println("regist task testG")
    it.doLast {
        println("TaskG do Last")
    }
}

task testA() {
    println("define task testA")
    dependsOn("testG")
    doLast {
        println("Task testA do Last")
    }
}
  • 控制台输出
E:\AndroidStudioProjects\ElectricityNoteLocal>gradlew -q testA
define task testA
regist task testG
BeforTask --- testG
TaskG do Last
AfterTask --- testG --- success
BeforTask --- testA
Task testA do Last
AfterTask --- testA --- success

说明:也可以通过向 TaskExecutionGraph 添加 TaskExecutionListener 监听来接收任务执行前通知和任务执行后通知(在 build.gradle 中通过 gradle.taskGraph.addTaskExecutionListener()添加)。