Gradle 学习之 Project

本文大部份内容来自 Gradle 官方文档,英文 OK 的同窗能够直接看官方文档。 Gradle 系列文章的示例代码都放在了 zjxstar 的 GitHub 上了。html

前言

本文假设你们都已经掌握了 Gradle 构建项目的目录结构。其中,脚本文件 settings.gradle 和 build.gradle 相当重要。Gradle 中的一系列构建操做都基于这两个文件。java

本系列文章想和你们一块儿学习 Gradle 的基础知识,如:Project、Task 和 Transform 等。本文的主要内容是 Project ,其中会涉及到一些简单的 Task,这里不会详细介绍 Task,你们只需知道有 Task 这个概念便可。文中的示例代码都基于 Android Studio 建立的标准 Android 项目。android

Project 的基本概念

在 Gradle 中,最重要的两个概念是 Project(项目) 和 Task(任务)。每一次构建都至少包含一个 Project,每个 Project 又包含了一个或多个 Task。每一个 build.gradle 文件都表明一个 Project,其实 Project 能够理解为在业务范围内,被抽象出来的一个个独立的模块。而 Task 通常被定义在 build.gradle 中,它表示一个操做,好比:复制文件、打个 jar 包、上传文件等。git

以 Android 项目举例说明:github

Android项目目录结构图

注:Demo 中使用的 Gradle 版本为 3.3.1 。闭包

使用 Android Studio 建立项目后,会自动生成两个模块,一个是以项目名命名的根模块,另外一个是 app 模块。两个模块中都有 build.gradle 文件,按照前文的说法,就是两个 Project。其中,项目 GradleSample 被称为 rootProject ,项目 app 被称为 subProject 。在根 build.gradle 文件中默认有一个名为 clean 的 Task,它的职责是删除根模块下 build 目录。app

在初始化构建过程当中,Gradle 会基于 build 脚本文件来组装 Project 和 Task。ide

Gradle 的构建生命周期

执行一个 Gradle 构建的最简单形式是执行一个 Task,而一些 Task 可能会依赖于其余 Task。Gradle 为了管理这些 Task 会在任何一个 Task 执行前构建一个 DAG 图(Directed Acyclic Graph,有向无环图)。这意味着全部的 Task 都会被一个接一个地执行,并且只执行一次。那些没有依赖的 Task 一般会被优先执行。学习

Gradle 的构建生命周期分为三个阶段:gradle

  • 初始化:Gradle 经过 settings.gradle 文件决定哪些项目参与构建,会建立 Project 实例。若是一个项目有多个模块,而且每一个模块都有其对应的 build.gradle 文件,那么就会建立多个 Project 实例。
  • 配置:这个阶段,构建脚本 build.gradle 会被执行,并为每一个 Project 实例建立和配置 Task。DAG 依赖关系图就在此阶段生成。
  • 执行:该阶段,Gradle 将决定哪一个任务会被执行。哪些任务被执行取决于开始此次构建的参数配置以及该 Gradle 文件的所在目录。

Project 的生命周期

在 Gradle 构建的初始化阶段,Gradle 会给每一个项目建立一个 Project 对象。那么 Project 又具备怎样的生命周期呢?

  • 为构建建立一个 Settings 实例。
  • 根据 Settings 对象评估 settings.gradle 脚本(若是存在)以进行配置。
  • 使用配置后的 Settings 对象建立 Project 实例的层次结构。
  • 最后,经过对项目执行 build.gradle文件(若是存在)来评估每一个 Project。Project 是以广度优先顺序进行评估的,这样就可使父 Project 在它的子 Project 以前完成评估。

Project 的属性

Gradle 针对 Project 实例执行项目的构建文件以配置项目。build 脚本中的任何属性和方法都会委托给关联的 Project 对象。这意味着,能够直接在脚本中使用 Project 接口上的任何属性和方法。

例如:

defaultTasks('some-task')  // Delegates to Project.defaultTasks()
reportsDir = file('reports') // Delegates to Project.file() and the Java Plugin
复制代码

你也能够经过 Project 实例使用 Project 属性,好比:

project.name // 获取project的名字,而不是单独使用name属性
复制代码

你能够在构建脚本中按照名称访问这些属性,也能够经过 Project 的

project.property(java.lang.String) // 例如:project.property('version')
复制代码

方法来进行访问。

一个项目在搜索属性时会考虑 5 个属性范围:

  • Project 对象自身。这个范围里的属性包含 Project 实现类中定义有 getters 和 setters 方法的全部属性。好比:project.getRootProject() 方法就对应了 rootProject 属性。至于这些属性的可读写性取决于它们是否认义 getters 或者 setters 方法了。
  • Project 的额外属性 ( extra ) 。每一个 Project 都会维护一个额外属性的映射,它能够包含任意的名称 -> 值对。定义后,此做用域的属性是可读写的。好比:project.ext.prop1 = 'foo' 。
  • 经过插件被添加到 Project 中的扩展属性 ( extensions ) 。每一个扩展名均可以做为只读属性使用,其名称与扩展名相同。好比:project.android.compileSdkVersion 。
  • 经过插件添加到 Project 中的约定属性 ( convention ) 。插件能够经过 Project 的 Convention 对象向 Project 中添加属性和方法。此范围的属性的可读可写性取决于约束对象。
  • Project 中 Tasks 。可使用 Task 的名称做为属性名称来访问任务。此范围的属性是只读的。

读写属性时,Project 都是按照上述范围的顺序进行查找的,在某个范围找到属性后就会返回该属性。若是没有找到,会抛出异常。

经常使用的 Project 属性有:

属性名 做用
allprojects 当前项目及其全部子项目的集合
buildDir 当前项目的编译目录(自动生成)
默认值 porjectDir/build
defaultTasks 当前项目的默认任务的名字集
当前构建没有提供任务名时会执行这些默认任务
group 当前项目的组名
logger 当前项目的日志器,能够用来在 build 文件中写日志
name 当前项目的名字
parent 当前项目的父项目
path 当前项目的路径(绝对路径)
project 当前项目的实例
rootProject 当前项目层次结构中的根项目
subprojects 当前项目的子项目集
tasks 当前项目的任务集
version 当前项目的版本号,默认值:unspecified

示例:使用 gradlew build 命令便可运行(基于第一小节图中的 Android 项目)。

// 访问Project的属性
// 在app模块的build.gradle中
println project.group // 打印组名:GradleSample
println version // 打印版本号,默认 unspecified
println project.name // 打印项目名:app
println rootProject.name // 打印根项目的名字:GradleSample
logger.quiet('Test project logger property') // 使用logger
for(String taskName : rootProject.defaultTasks) { // 获取默认任务
    println "defaultTask: $taskName" // 打印:defaultTask: clean
}
println rootProject.ext.hello // 打印额外参数

// 在根build.gradle脚本中
defaultTasks 'clean' // 设置默认Task
ext { // 设置额外参数
    hello = 'Welcome to Gradle'
}
复制代码

Project 的方法

一个项目在搜索方法时,也会考虑 5 个范围:

  • Project 对象自身。
  • build.gradle 脚本文件。
  • 经过插件添加到 Project 中的扩展 ( extensions ) 。每一个扩展均可以当作参数是闭包或 Action 的方法。
  • 插件添加到项目中的约定方法 ( convention ) 。插件能够经过项目的 Convention 对象向项目中添加属性和方法。
  • 项目中的 Tasks 。每一个 Task 都会添加一个方法,方法名是任务名,参数是单个闭包或者 Action 。该方法使用提供的闭包为相关任务调用 Task.configure( groovy.lang.Closure ) 方法。

经常使用的 Project 方法有:

方法名(不列出参数,部分方法有重载) 做用
afterEvaluate 能够添加一个闭包,它会在项目完成评估后当即执行。
当执行属于该项目的构建文件时,会通知此类监听器。
allprojects 配置当前项目以及它的每一个子项目
apply 应用插件或脚本
beforeEvaluate 添加一个闭包,她会在项目开始评估前当即执行
configure 经过闭包配置对象集合
copy 拷贝特定文件
file 解析文件
findProperty 找特定属性,返回它的值,若是没有,返回null
hasProperty 判断当前项目有没有指定属性
project 获取指定项目的 project 对象
setProperty 给属性设置值
subprojects 配置当前项目的全部子项目
task 定义一个任务

示例:使用 gradlew -q helloTask 命令运行便可。

// 根build.gradle
allprojects {
    // 给全部项目都添加一个任务:helloTask
    task helloTask {
        doLast { task ->
            println "I'm ${task.project.name}"
        }
    }
}

subprojects {
    helloTask { // 给全部子项目中的helloTask任务增长一个doFirst回调
        doFirst {
            println "I'm the task in sub project, doFirst"
        }
    }

    afterEvaluate { project ->
        println "it's after evaluate..."
        helloTask.configure {
            doLast {
                println "configure task after evaluate"
            }
        }
    }
}

// 给全部的子项目(除了app项目)的helloTask配置doLast
configure(subprojects.findAll{ it.name != 'app' }) {
    helloTask {
        doLast {
            println "i am not app module, i configure doLast"
        }
    }
}
复制代码

输出结果:

it's after evaluate...
I'm GradleSample
I'm the task in sub project, doFirst
I'm app
configure task after evaluate
复制代码

总结

本文的内容很少,详细介绍了 Gradle 中 Project 的生命周期、属性和方法。同时,概述了 Gradle 构建项目的三个时期,并提到了 Task 。其实,Gradle 主要仍是围绕 Task 来运转的,后续文章咱们将学习 Task 的一系列知识。

参考资料

  1. Gradle 官方文档
  2. Android 官网
  3. 《Android Gradle权威指南》
  4. 《Gradle for Android 中文版》
相关文章
相关标签/搜索