以下内容全部来自各种摘抄
1. Gradle生命周期
gradle构建过程分为三部分: 初始化阶段、配置阶段和执行阶段
1.1 初始化阶段
初始化阶段的任务是创建项目的层次结构, 并且为每一个项目创建一个Project实例, 与初始化阶段相关的脚本文件是settings.gradle, 一个settings.gradle脚本对应一个Settings对象
1.2 配置阶段
配置阶段的任务是执行各项目下的build.gradle脚本, 完成Project的配置, 并且构造Task任务依赖关系图以便在执行阶段按照依赖关系执行task. 每个build.gradle脚本文件对应一个Project对象, 在初始化阶段创建.
1.3 执行阶段
在配置阶段结束后, Gradle会根据task的依赖关系创建一个有向无环图, 可以通过Gradle对象的getTaskGraph方法访问, 对应的类为TaskExecutionGraph.
2. beforeEvaluate
初始化结束之后, Project开始配置之前的回调
3. 挂接到构建生命周期
gradle提供了构建生命周期的钩子来支持执行构建生命周期中某事件发生时想要执行的代码, 一个生命周期事件可以在某个构建阶段之前、期间或者之后. 在执行阶段之后发生的生命周期事件是构建的完成.
有两种方式可以编写回调生命周期事件:- 将代码写在闭包中
- 实现Gradle API所提供的监听器接口
3.1 Task执行图
在配置阶段, gradle决定了在执行阶段要运行的task顺序, 这个顺序被建模为一个表示依赖关系的有向无环图(DAG), 图中的每个task被称为一个节点, 且该图没有闭环, 也就是说先前执行的task不会再次执行.
3.2 挂接到task执行图
gradle api对task执行图有对应的领域模型对象, 可以通过project.gradle.taskGraph来访问task执行图对象
实现taskGraph监听器来挂接
通过监听器挂接到生命周期需要两步:
- 编写一个类来实现特定的监听器接口 用于监听taskGraph事件的接口是由TaskExecutionGraphListener接口提供的, 需要实现TaskExecutionGraphListener接口的graphPopulated(TaskExecutionGraph)方法, 具体用法如下:
class DoKitTransformTaskExecutionListener(private var project: Project) : TaskExecutionAdapter() {
override fun beforeExecute(task: Task) {
task.takeIf {
it.project == project && it is TransformTask && it.transform.scopes.isNotEmpty()
}?.run {
task["outputStream"]?.call<Unit>("init")
}
}
}
// 在监听器中不能直接访问Project实例, 但是可以通过GradleAPI, 通过task来访问该task所属的project.
project.gradle.addListener(DoKitTransformTaskExecutionListener(project))
2.字符串
在Groovy中, 单引号和双引号都可以定义一个字符串常量, 区别是单引号标记的是纯粹的 字符串常量, 而不是对字符串里的表达式做运行, 但是双引号可以. 代码实现如下
task printStringVar {
def name = "张三"
println '单引号的变量计算:&{name}'
println "双引号的变量计算:&{name}"
}
输出结果:
单引号的变量计算:${name}
双引号的变量计算:张三
2.集合List、Map
task printList {
def numList = [1, 2, 3, 4, 5]
println numList[0]
println numList[1..3]
numList.each {
println it
}
def map1 = ['width': 1024, 'height': 123]
map1.each {
println "key:${it.key}, value:${it.value}"
}
}
输出:
1
[2, 3, 4]
1
2
3
4
5
key:width, value:1024
key:height, value:123
3.方法
task printMethod {
method (1, 2)
method 1, 2 // 方法调用可以省略()
}
def method(int a, int b) {
println a + b
}
4.return可以不写
当没有return时, Groovy会把 方法执行过程中的最后一句代码作为其返回值, 注意是方法执行过程中的最后一句代码, 而不是源码中的最后一行代码
task printReturn {
def add1 = method 1, 2
def add2 = method 3, 2
println "add1: $add1, add2: $add2"
}
def method(int a, int b) {
if (a > b) {
a
} else {
b
}
}
输出:
add1: 2, add2: 3
5.代码块可以作为参数传递
三种写法:
task printMethod {
def numList = [1, 2, 3, 4, 5]
println "写法一:"
numList.each({
print it + ", "
})
println()
println "写法二:"
numList.each() {
print it + ", "
}
println()
println "写法三:"
numList.each {
print it + ", "
}
}
输出:
写法一:
1, 2, 3, 4, 5,
写法二:
1, 2, 3, 4, 5,
写法三:
1, 2, 3, 4, 5,
6.闭包
6.1 闭包的it变量
task testClosure {
println()
println "闭包closure it变量"
customEach {
print it + ", "
}
println()
}
def customEach(closure) {
for (int i in 1..10) {
closure(i)
}
}
输出:
闭包closure it变量
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
闭包就是一段代码块, 定义的customEach方法只有一个参数, 用于接收一个闭包(代码块), 闭包跟一对括号就是执行了, 括号里面的参数就是该闭包接收的参数, 如果只有一个参数, 那么就是it变量
6.2 向闭包传递参数
当闭包只有一个参数时, 默认就是it, 当有多个参数时, it就不能表示了, 需要把参数一一列出:
task closureMap {
eachMap { k, v ->
println "key: ${k}, value: ${v}"
}
}
static def eachMap(closure) {
def map = ["param1": "name", "param2": "age"]
map.each {
println "it: " + it
closure(it.key, it.value)
}
}
输出:
it: param1=name
key: param1, value: name
it: param2=age
key: param2, value: age
为闭包传递了两个参数, 一个key, 另一个value. 这时闭包调用就不能使用it了, 必须要显示声明出来, 例子中k、v、->用于把闭包的参数和主体区分开来
6.3 闭包委托
Groovy的闭包有thisObject、owner、delegate三个属性, 当在闭包内部调用方法时, 由它们来确定使用哪个对象来处理. 默认情况下delegate和owner是相等的, 但是delegate是可以被修改的, Gradle中的闭包的很多功能都是通过修改delete来实现的.
task delegateTest {
new Delegate().test {
println "thisObject: ${thisObject.getClass()}"
println "owner: ${owner.getClass()}"
println "delegate: ${delegate.getClass()}"
method1()
it.method1()
}
}
def method1() {
println "Context this: ${this.getClass()} in root"
println "method1 in root"
}
class Delegate {
def method1() {
println "Delegate this: ${this.getClass()} in Delegate"
println "method1 in Delegate"
}
def test(Closure<Delegate> closure) {
closure(this)
}
}
输出:
thisObject: class build_e767dje1933oj6rshd41zzldb
owner: class build_e767dje1933oj6rshd41zzldb$_run_closure6
delegate: class build_e767dje1933oj6rshd41zzldb$_run_closure6
Context this: class build_e767dje1933oj6rshd41zzldb in root
method1 in root
Delegate this: class Delegate in Delegate
method1 in Delegate
thisObject的优先级最高, 默认情况下, 优先使用thisObject来处理闭包中调用的方法, 如果有则执行, thisObject是当前构建脚本的上下文, 它和脚本中的this对象是相等的
delegate和owner是相等的, 它们两个的优先级是: owner要比delegate高, 所以闭包内方法的处理顺序是: thisObject > owner > delegate
一般会指定delegate为当前的it, 这样在闭包内就可以对该it进行配置, 或者调用其方法
task configClosure {
person {
personName = "张三"
personAge = 20
dumpPerson()
}
}
class Person {
String personName
int personAge
def dumpPerson() {
println "name is $personName, age is $personAge"
}
}
def person(Closure<Person> closure) {
Person p = new Person()
closure.delegate = p
closure.setResolveStrategy(Closure.DELEGATE_FIRST)
closure(p)
}
输出:
name is 张三, age is 20
7.task
一个Project是由多个Task组成的, Task是一个原子性的操作, 比如打个jar包, 复制一份文件, 编译一次java代码, 上传一个jar到Maven中心库等, 这就是一个Task.
7.1 使用Task创建任务
task customTask {
doFirst {
println 'customTask: doFirst'
}
}
这里的task是Project的一个函数, 原型为create(String name, Closure configureClosure), customTask是任务的名字, 可以自定义, 第二个参数是一个闭包. 创建任务的两种方式
方式一:
task customTask1 {
println 'customTask1'
}
方式二:
通过TaskContainer创建任务, 在Gradle里, Project对象已经帮我们定义好了一个TaskContainer, 方便使用, 这就是tasks
tasks.create("customTask2") {
println 'customTask2'
}
7.2 自定义属性
Project和Task都运行用户添加额外的自定义属性, 要添加额外的属性, 通过应用所属对应的ext属性即可实现. 添加之后可以通过ext属性对自定义属性读取和设置, 如果要同时添加多个自定义属性, 可以通过ext代码块:
// 自定义一个Project的属性
ext.age = 18
// 通过代码块同时自定义多个属性
ext {
phone = 123456789
address = "地址"
}
task extTest {
println "age: $age"
println 'phone: $phone'
println 'address: $address'
}
输出:
age: 18
phone: 123456789
address: 地址
相比局部变量, 自定义属性有更为广泛的作用域, 你可以跨Project, 跨Task访问这些自定义属性, 只要你能访问这些属性所属的对象, 就可以访问到这些属性.
自定义属性不仅仅局限在Project和Task上, 还可以应用在SourceSet中, 这样等于每种SourceSet又多了一个可供配置的属性.7.2.1 代码用例
apply plugin: "java"
// 1.自定义Project属性
ext.age = 18
// 2.通过代码块同时自定义多个属性
ext {
phone = 123456
address = "address"
}
sourceSets.all {
ext.resourcesDir = null
}
sourceSets {
main {
resourcesDir = 'main/res'
}
test {
resourcesDir = 'test/res'
}
}
task extCustomProperty {
println "年龄: $age"
println "电话: $phone"
println "地址: $address"
sourceSets.each {
println "it: ${it}"
println "${it.name}的resourcesDir是: ${it.resourcesDir}"
}
}
7.2.2 输出结果
年龄: 18
电话: 123456789
地址: address
it: source set 'main'
main的resourcesDir是: main/res
it: source set 'test'
test的resourcesDir是: test/res
7.3 sourceSets
8.afterEvaluate
gradle提供了对project状态配置监听的接口回调, 以方便来配置一些Project的配置属性, 监听主要分为两大类, 一种是通过project进行回调, 一种是通过gradle进行回调, 作用域也不同, project只针对当前project进行监听, gradle监听是针对所有的project而言.
afterEvaluate是对project配置状态进行的回调, 只要project配置成功均会调用.
this.project.afterEvaluate {
println 'this.project.afterEvaluate'
// 这里可以添加一些版本控制信息
}
8.1 执行顺序
Gradle执行的时候遵循如下顺序:
- 1、首先解析settings.gradle来获取模块信息,这是初始化阶段;
- 2、然后配置每个模块,
配置的时候并不会执行task; - 3、配置完了以后,有一个重要的回调project.afterEvaluate,它表示所有的模块都已经配置完了,可以准备执行task了;
- 4、执行指定的task。
备注:如果注册了多个project.afterEvaluate回调,那么执行顺序等同于注册顺序。在上面的例子中,由于buildSrc中的回调注册较早,所以它也先执行。
9.Extension
一句话概括, 把理解成java中的java bean, 装载数据, 注册到ExtensionContainer中, 需要用时, 从ExtensionContainer中取出.
用时查gradle api文档即可
9.1 create 创建Extension
<T> T create(String name, Class<T> type, Object... constructionArguments)
- name:要创建的Extension的名字,可以是任意符合命名规则的字符串,不能与已有的重复,否则会抛异常;
- type:该Extension的类类型;
9.2 add 增加Extension
前面的 create() 方法会创建并返回一个 Extension 对象,与之相似的还有一个 add() 方法,唯一的差别是它并不会返回一个 Extension 对象。
void add(String name, T extension)
9.3 get获取Extension
Object findByName(String name)
<T> T findByType(Class<T> type)
Object getByName(String name) //找不到会抛异常
<T> T getByType(Class<T> type) //找不到会抛异常
- name: 通过名字取查找
- type: 通过类类型取查找
10. ext
// ext本质是一个map
Map<String, Object> properties (read-only)
使用方式例如:
// 在project的build目录下通过ext配置工程的相关信息
ext {
minSdkVersion = 19
targetSdkVersion = 28
compileSdkVersion = 28
buildToolsVersion = '28.0.3'
}
//在module的build中引用
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
}
}