文章目录
- 环境配置
- [一. 文章学习笔记](#一. 文章学习笔记)
-
- [1. Gradle 系列 (一)、Gradle相关概念理解,Groovy基础](#1. Gradle 系列 (一)、Gradle相关概念理解,Groovy基础)
-
- [1.1 Gradle 是什么](#1.1 Gradle 是什么)
- [1.2 DSL是什么](#1.2 DSL是什么)
- [1.3 什么是 Groovy](#1.3 什么是 Groovy)
- [1.4 Grovy基础语法](#1.4 Grovy基础语法)
-
- [支持动态类型,使用 def 关键字来定义一个变量](#支持动态类型,使用 def 关键字来定义一个变量)
- [不用写 ; 号](#不用写 ; 号)
- 没有基本数据类型了,全是引用类型
- 方法变化
- 字符串变化
- [不用写 get 和 set 方法](#不用写 get 和 set 方法)
- [Class 是一等公民,所有的 Class 类型可以省略 .Class](#Class 是一等公民,所有的 Class 类型可以省略 .Class)
- [== 和 equals](#== 和 equals)
- [使用 assert 来设置断言,当断言的条件为 false 时,程序将会抛出异常](#使用 assert 来设置断言,当断言的条件为 false 时,程序将会抛出异常)
- [支持 ** 次方运算符](#支持 ** 次方运算符)
- [简洁的三元表达式 ?:](#简洁的三元表达式 ?:)
- [简洁的非空判断 ?.](#简洁的非空判断 ?.)
- [强大的 Switch](#强大的 Switch)
- [判断是否为 null 和 非运算符](#判断是否为 null 和 非运算符)
- [可以使用 Number 类去替代 float、double 等类型,省去考虑精度的麻烦](#可以使用 Number 类去替代 float、double 等类型,省去考虑精度的麻烦)
- [默认是 public 权限](#默认是 public 权限)
- 使用命名的参数初始化和默认的构造器
- [使用 with 函数操作同一个对象的多个属性和方法](#使用 with 函数操作同一个对象的多个属性和方法)
- 异常捕获
- [1.5 Groovy 闭包](#1.5 Groovy 闭包)
- [1.6 Groovy数据结构](#1.6 Groovy数据结构)
- [2. Gradle 系列 (二)、Gradle 技术探索](#2. Gradle 系列 (二)、Gradle 技术探索)
- [2.2 Gradle生命周期 Hook 点](#2.2 Gradle生命周期 Hook 点)
环境配置
一. 文章学习笔记
Gradle 系列 (一)、Gradle相关概念理解,Groovy基础
1. Gradle 系列 (一)、Gradle相关概念理解,Groovy基础
1.1 Gradle 是什么
Gradle 是一个开源的自动化构建工具 ,专注于灵活性和性能。Gradle 构建脚本是使用 Groovy 或 Kotlin DSL 编写的。
Gradle 是 Android 的默认构建工具,Android 项目这么多东西,既有我们自己写的 java、kotlin、C++、Dart 代码,也有系统自己的 java、C,C++ 代码,还有引入的第三方代码,还有多媒体资源,这么多代码、资源打包成 APK 文件肯定要有一个规范,干这个活的就是我们熟悉的 gradle 了,总而言之,Gradle就是一个帮我们打包 APK 的工具

1.2 DSL是什么
domain specific language,中文翻译即领域特定语言
解决特定领域的专有问题
比如 Java 语言希望能做 Android 开发,又希望能做后台开发,它具有横向扩展的特性。而 DSL 具有纵向深入解决特定领域专有问题的特性。
Gradle 的 DSL 目前支持两种语言的格式,Groovy 和 Kotlin,Kotlin 格式的 DSL 是在 5.0 引入的
1.3 什么是 Groovy
Groovy 是基于 JVM 的脚本语言 ,它是基于Java扩展的动态语言
基于 JVM 的语言有很多种,如:Groovy,Kotlin,Java,Scala等等,他们都拥有一个共同的特性:最终都会编译生成 Java 字节码文件并在 JVM 上运行。
1.4 Grovy基础语法
支持动态类型,使用 def 关键字来定义一个变量
java
//Java 中,我们一般会这么定义
int age = 16
String name = "erdai"
//Groovy 中,我们可以这样定义,在变量赋值后,Groovy 编译器会推断出变量的实际类型
def age = 16
def name = 'erdai'
不用写 ; 号
没有基本数据类型了,全是引用类型
方法变化
- 使用 def 关键字定义一个方法 ,方法不需要指定返回值类型,参数类型 ,方法体内的最后一行会自动作为返回值 ,而不需要return关键字
- 方法调用可以不写 () ,最好还是加上 () 的好,不然可读性不好
- 定义方法时,如果参数没有返回值类型,我们可以省略 def,使用 void 即可
- 实际上不管有没有返回值,Groovy 中返回的都是 Object 类型
- 类的构造方法 ,避免添加 def 关键字
java
def sum(a,b){
a + b
}
def sum = sum(1,2) //还可以写成这样,但是可读性不好 def sum = sum 1,2
println(sum)
//打印结果
3
//如果方法没有返回值,我们可以这样写:
void doSomething(param1, param2) {
}
//类的构造方法,避免添加 def 关键字
class MyClass {
MyClass() {
}
}
字符串变化
可扩展字符串 的含义,可扩展字符串就是字符串里面可以引用变量,表达式等等
1 、单引号 '' 定义的字符串为不可扩展字符串
2 、双引号 "" 定义的字符串为可扩展字符串 ,可扩展字符串里面可以使用 ${} 引用变量值,当 {} 里面只有一个变量,非表达式时,{}也可以去掉
3 、三引号 ''' ''' 定义的字符串为输出带格式的不可扩展字符串
不用写 get 和 set 方法
- 在我们创建属性的时候,Groovy会帮我们自动创建 get 和 set 方法
- 当我们定义了一个属性的 get 方法,而没有定义这个属性,默认这个属性只读
- 如果我们不想调用这个特殊的 get 方法时则可以使用 .@ 直接域访问操作符访问属性本身
java
//情况1:在我们创建属性的时候,Groovy会帮我们自动创建 get 和 set 方法
class People{
def name
def age
}
def people = new People()
people.name = 'erdai'
people.age = 19
println "姓名: $people.name 年龄: $people.age"
//打印结果
姓名: erdai 年龄: 19
//情况2 当我们定义了一个属性的 get 方法,而没有定义这个属性,默认这个属性只读
//我们修改一下People类
class People{
def name
def getAge(){
12
}
}
def people = new People()
people.name = 'erdai'
people.age = 19
println "姓名: $people.name 年龄: $people.age"
//运行一下代码 打印结果报错了,如下:
Caught: groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: age for class: variable.People
//大概错误意思就是我们不能修改一个只读的属性
//情况3: 如果我们不想调用这个特殊的 get 方法时则可以使用 .@ 直接域访问操作符访问属性本身
class People{
def name
def age
def getName(){
"My name is $name"
}
}
//这里使用了命名的参数初始化和默认的构造器创建people对象,后面会讲到
def people = new People(name: 'erdai666')
people.age = 19
def myName = people.@name
//打印值
println myName
println "姓名: $people.name 年龄: $people.age"
//打印结果
erdai666
姓名: My name is erdai666 年龄: 19
//看到区别了吗?使用 people.name 则会去调用这个属性的get方法,而 people.@name 则会访问这个属性本身
Class 是一等公民,所有的 Class 类型可以省略 .Class
java
//定义一个Test类
class Test{
}
//定义一个测试class的方法,从前面的语法我们知道,方法的参数类型是可以省略的
def testClass(myClass){
}
//测试
testClass(Test.class)
testClass(Test)
== 和 equals
== 就相当于 Java 的 equals,如果需要比较两个对象是否是同一个,需要使用 .is()
使用 assert 来设置断言,当断言的条件为 false 时,程序将会抛出异常
支持 ** 次方运算符
简洁的三元表达式 ?:
简洁的非空判断 ?.
强大的 Switch
java
def result = 'erdai666'
switch (result){
case [1,2,'erdai666']:
println "匹配到了result"
break
default:
println 'default'
break
}
//打印结果
匹配到了result
判断是否为 null 和 非运算符
在 Groovy 中,所有类型都能转成布尔值,比如 null 就相当于0或者相当于false,其他则相当于true
java
//在 Java 中,我们会这么用
if (name != null && name.length > 0) {
}
//在 Groovy 中,可以这么用,如果name为 null 或 0 则返回 false,否则返回true
if(name){
}
//非运算符 erdai 这个字符串为非 null ,因此为true,而 !erdai 则为false
assert (!'erdai') = false
可以使用 Number 类去替代 float、double 等类型,省去考虑精度的麻烦
默认是 public 权限
使用命名的参数初始化和默认的构造器
java
//定义一个people
class People{
def name
def age
}
//我们可以通过以下几种方式去实例化一个对象,注意我们People类里面没有写任何一个构造方法哦
def people1 = new People()
def people1 = new People(age: 15)
def people2 = new People(name: 'erdai')
def people3 = new People(age: 15,name: 'erdai')
使用 with 函数操作同一个对象的多个属性和方法
异常捕获
java
//在 java 中我们会这样写
try {
// ...
} catch (Exception e) {
// do something
}
//在 Groovy 中,我们可以这样写
try {
// ...
} catch (any) {
// do something
}
1.5 Groovy 闭包
定义
Groovy 中的闭包是一个开放的、匿名的代码块 ,它可以接受参数、返回值并将值赋给变量
声明
1、闭包基本的语法结构:外面一对大括号,接着是申明参数,参数类型可省略,在是一个 -> 箭头号,最后就是闭包体里面的内容
2、闭包也可以不定义参数,如果闭包没定义参数的话,则隐含有一个参数,这个参数名字叫 it
java
//1
{ params ->
//do something
}
//2
{
//do something
}
调用
1、闭包可以通过 .call 方法来调用
2、闭包可以直接用括号+参数来调用
闭包中的关键变量this、owner 和 delegate
- getThisObject() 方法 和 thisObject 属性等同于 this
this 永远指向定义该闭包最近的类对象,就近原则,定义闭包时,哪个类离的最近就指向哪个,我这里的离得近是指定义闭包的这个类,包含内部类 - getOwner() 方法 等同于 owner
owner 永远指向定义该闭包的类对象或者闭包对象,顾名思义,闭包只能定义在类中或者闭包中 - getDelegate() 方法 等同于 delegate
delegate 和 owner 是一样的 ,我们在闭包的源码中可以看到,owner 会把自己的值赋给 delegate,但同时 delegate 也可以赋其他值
this 和 owner 默认是只读的,我们外部修改不了它,这点在源码中也有体现,但是可以对 delegate 进行操作
java
def outerClosure = {
def innerClosure = {
println "this: " + this
println "owner: " + owner
println "delegate: " + delegate
}
innerClosure.call()
}
println outerClosure
outerClosure.call()
//打印结果如下
variable.GroovyGrammar$_run_closure4@64beebb7
this: variable.GroovyGrammar@5b58ed3c
owner: variable.GroovyGrammar$_run_closure4@64beebb7
delegate: variable.GroovyGrammar$_run_closure4@64beebb7
//结果证明 this 指向了当前GroovyGrammar这个脚本类对象 owner 和 delegate 都指向了 outerClosure 闭包对象
闭包委托策略
闭包的默认委托策略是 OWNER_FIRST,也就是闭包会先从 owner 上寻找属性或方法 ,找不到则在 delegate 上寻找
- 其他策略
OWNER_FIRST:默认策略,首先从 owner 上寻找属性或方法,找不到则在 delegate 上寻找
DELEGATE_FIRST:和上面相反,首先从 delegate 上寻找属性或者方法,找不到则在 owner 上寻找
OWNER_ONLY:只在 owner 上寻找,delegate 被忽略
DELEGATE_ONLY:和上面相反,只在 delegate 上寻找,owner 被忽略
TO_SELF:高级选项,让开发者自定义策略,必须要自定义实现一个 Closure 类,一般我们这种玩家用不到
java
//创建一个香蕉类
class Banana{
def name
}
//创建一个橘子类
class Orange{
def name
}
//定义一个香蕉对象
def banana = new Orange(name: '香蕉')
//定义一个橘子对象
def orange = new Orange(name: '橘子')
//定义一个闭包对象
def closure = {
//打印值
println delegate.name
}
//调用闭包
closure.call()
//运行一下,发现结果报错了,如下
Caught: groovy.lang.MissingPropertyException: No such property: name for class: variable.GroovyGrammar
//大致意思就是GroovyGrammar这个脚本类对象没有这个 name 对象
//修改闭包的delegate
closure.delegate = orange
//我们在运行一下,打印结果:
橘子
1.6 Groovy数据结构
数组
as关键字
java
def groovyArray = ["Java", "Groovy", "Android"] as String[]
列表
java
def list2 = ['erdai666', 1, true]
def list4 = [1, 2, 3] as LinkedList
list.add(20)
list[0] = 100
list.remove(0)
list.find {
println it
}
Map
java
def map = [a: 1, 'b': true, "c" : "Groovy", '''d''' : '''ddd''']
// 元素访问操作
println map.a
println map['b']
println map.get('c')
// 添加和修改元素
map.put('key','value')
map['key'] = "value"
Range
java
def range = 1..10
//如果不想包含最后一个元素
def range1 = 1..<10
2. Gradle 系列 (二)、Gradle 技术探索
2.1 Gradle 构建流程
初始化阶段,配置阶段,执行阶段


初始化阶段
- 执行 settings.gradle 脚本,构建 Project 对象
- 如何引用任何位置下的工程
Settings 是一个对象,include 就是 Settings 对象下的一个方法。其他API见链接 添加链接描述
java
include ':test'
//手动设置 :test 模块的项目目录(即源码和 build.gradle 所在目录)为指定路径。
project(':test').projectDir = file('当前工程的绝对路径')

AAR 和源码的快速切换
java
//步骤一:在 settings.gradle 中引入源码工程
include ':test'
project(':test').projectDir = file('当前工程的绝对路径')
//步骤二:在根 build.gradle 下进行如下配置
// 步骤二:在根 build.gradle 下进行如下配置
// allprojects 表示对当前项目及其所有子项目生效
allprojects {
// 遍历所有配置(compile, runtime 等)
configurations.all {
// resolutionStrategy 定义了依赖解析策略
resolutionStrategy {
// dependencySubstitution 允许替换依赖项
dependencySubstitution {
// 将所有对 com.dream:test 的依赖替换为本地项目 :test
substitute module("com.dream:test") with project(':test')
}
}
}
}
配置阶段
Gradle 配置阶段主要就是解析 Project 对象(build.gradle 脚本文件) ,构建 Task 有向无环图
配置阶段会执行的代码:除 Task 的 Action 中编写的代码都会被执行(Action 中的代码(即 doFirst、doLast 或 << 操作符) 是在 执行阶段 才运行的代码。)。如:
1、build.gradle 中的各种语句
2、Task 配置段语句
配置阶段完成后,整个工程的 Task 依赖关系都确定了,我们可以通过 Gradle 对象的 getTaskGraph 方法访问 Task ,对应的类为 TaskExecutionGraph
执行阶段
Gradle 执行阶段主要就是执行 Task 及其依赖的 Task