前言
在 Android 中现在默认的构建工具是 Gradle ,而 Gradle 构建工具是使用 Groovy 语言编写的,这就使得了解 Groovy 语言变得非常重要了。
如果你不了解 Groovy 会以为 build.gralde 文件中写的就只是一些配置,其实并没有这么简单。之所以看起来项配置文件,是因为 Groovy 的闭包特性,让我们写起来简单,看起来像配置文件,这就降低了编写的门槛。使得我们可以专注于 Android 层面的业务逻辑。
随着 Android 的发展,我们不能局限于应用层,在构建的过程我们也可以进行一些优化。今天就来学习一下 Groovy 这们语言。
安装
安装很简单,我们可以从Groovy官网下载安装包,也可以通过包管理器来安装, macOS 下可以通过如下命令安装
1
2
3
|
brew install groovy
# 下面这条指令也可以,根据自己的系统选择一个常用的就行
curl -s get.sdkman.io | bash
|
安装好之后我们可以通过如下指令验证是否安装成功
1
2
|
groovy --version
Groovy Version: 3.0.3 JVM: 1.8.0_45 Vendor: Oracle Corporation OS: Mac OS X
|
可以看到输出了 groovy 的版本。
基础语法
变量
在 groovy 中声明变量有两种,一种是自动推导。编译器会根据你写的字面量去推导出合适的类型,自动推导的都是对象类型,不是基本数据类型。
如果你要使用基本数据类型,就只能直接指定变量的类型。如下所示
1
2
3
4
5
6
7
8
|
// 自动推导
def name = "groovy"
// 指定变量类型
int age = 3
println name.class
println age.class
|
输出结果如下
1
2
|
class java.lang.String
class java.lang.Integer
|
可以看到即便我们指定了变量的类型,最后还是会被装换成对象类型。
这里还要一个地方要注意的就是,浮点数会被推导为 BigDecimal 而不是 Double
1
2
3
|
def pi = 3.14
println pi.class
|
输出如下所示
1
|
class java.math.BigDecimal
|
字符串
groovy 中的字符串与 Java 中的不同,更像是 Python 中的字符串,支持单引号,双引号,三引号的写法。
1
2
3
4
5
6
7
8
9
10
11
|
def str1 = 'str1'
def str2 = "str2"
def str3 = '''str3 line1
str3 line2
str3 line3'''
def str4 = """
str4 line2
str4 line3
str4 line4
"""
|
输出如下所示
1
2
3
4
5
6
7
8
9
|
str1
str2
str3 line1
str3 line2
str3 line3
str4 line2
str4 line3
str4 line4
|
其中双引号可以支持在字符串中引用其他变量,这时候的类型为 GStringImpl
1
2
3
4
5
6
7
|
def name = "groovy"
def age = 3
def info = "the ${name} age is: ${age}"
println info
println info.class
|
输出如下所示
1
2
|
the groovy age is: 3
class org.codehaus.groovy.runtime.GStringImpl
|
可以看到通过 $ 可以引用别的变量,这时候字符串的类型为 org.codehaus.groovy.runtime.GStringImpl 而不再是 String 了。它们之间可以相互赋值使用,不需要强制类型转换。
使用这种方式拼接字符串就很方便,非常直观,不用写一堆的 + 。这中方式也叫模板字符串。在 ${} 中还可以进行求值计算。
闭包
简单使用
闭包和 Java 中的 lambda 表达式类似
1
2
3
4
5
6
7
|
def closure = {
println "Hello Groovy Closure"
}
closure()
closure.call()
|
输出如下所示
1
2
|
Hello Groovy Closure
Hello Groovy Closure
|
可以看到闭包就像一个函数一样,可以通过函数调用也可以通过 call() 来调用。
带有参数的闭包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
// 1 带有参数的闭包
def add = { x, y ->
x + y
}
println add(1,2)
// 参数有类型
def add1 = { int x, int y ->
x + y
}
println add(1,2)
// 参数有返回值
def add2 = { int x, int y ->
return x + y
}
println add2(1,2)
def increment = {
it + 1
}
println increment(1)
|
输出结果如下
1
2
3
4
|
the add(1,2) result is: 3
the add1(1,2) result is: 3
the add2(1,2) result is: 3
the increment(1) result is: 2
|
闭包的参数类型可以不指定,闭包的最后一行代码的执行结果默认会作为返回值。
如果闭包只有一个参数,我们可以不指定参数名,闭包会提供一个 it 来代替。
闭包委托策略
闭包在 groovy 中也是一个对象,其类名是 groovy.lang.Closure 。闭包的委托策略是其独有的,再了解委托策略之前我们先来了解一下闭包中的 this 、 owner 、 delegate
- this: 表示闭包在哪个类定义的
- owner: 表示闭包直接定义的地方,可以是个闭包或者类,这种情况通常是在闭包嵌套的时候会和
this 不一样
- delegate: 用来确定方法的调用者或者属性是哪个对象的,默认和
owner 一致。代表的是第三方对象,在这个第三方对象中你可以找到闭包中未定义的方法调用或者属性。
先来看个例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
def closure = {
println "this is:" + this
println "owner is:" + owner
println "delegate is:" + delegate
def innerClosure = {
println "innerClosure this is:" + this
println "innerClosure owner is:" + owner
println "innerClosure delegate is:" + delegate
}
innerClosure()
}
closure()
|
输出结果如下
1
2
3
4
5
6
|
this is:Main@7fcbe147
owner is:Main@7fcbe147
delegate is:Main@7fcbe147
innerClosure this is:Main@7fcbe147
innerClosure owner is:Main$_run_closure1@36b0fcd5
innerClosure delegate is:Main$_run_closure1@36b0fcd5
|
可以看到默认它们都指向同一个对象。当闭包嵌套的时候 this 与 owner 、 delegate 不同。
既然在嵌套的时候 this 与 owner 不同,那什么时候 owner 与 delegate 不同?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Person {
}
def closure = {
println "this is:" + this
println "owner is:" + owner
println "delegate is:" + delegate
}
def p = new Person()
closure.delegate = p
closure()
|
输出如下所示
1
2
3
|
this is:Main@410ae9a3
owner is:Main@410ae9a3
delegate is:Person@25ddbbbb
|
delegate 指向了 Person 对象并且 delegate 是可以被修改的,而 this 和 owner 则不可以被修改。那 delegate 有什么用呢?来看下面的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Person {
String name
def pretty = { "My name is $name" }
String toString() {
pretty()
}
}
class Thing {
String name
}
def p = new Person(name: 'Groovy')
def t = new Thing(name: 'Java')
println p.toString()
p.pretty.delegate = t
println p.toString()
|
输出结果如下
1
2
|
My name is Groovy
My name is Groovy
|
可以看到我们改了 pretty 闭包的 delegate 但是并没有任何效果。这是因为 delegate 的默认策略是 Closure.OWNER_FIRST ,所以没有生效。
我们可以通过更改 delegate 的 resolveStrategy 为 Closure.DELEGATE_FIRST 来使其生效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Person {
String name
def pretty = { "My name is $name" }
String toString() {
pretty()
}
}
class Thing {
String name
}
def p = new Person(name: 'Groovy')
def t = new Thing(name: 'Java')
println p.toString()
p.pretty.delegate = t
// 修改 delegate 策略
p.pretty.resolveStrategy = Closure.DELEGATE_FIRST
println p.toString()
|
输出结果如下
1
2
|
My name is Groovy
My name is Java
|
这次更改就生效了。
resolveStrategy 可以有如下几种策略
Closure.OWNER_FIRST 默认为这个策略,先从 owner 中找属性或方法,找不到再从 delegete 中寻找。
Closure.DELEGATE_FIRST 先从 delegate 中查找,与 OWNER_FIRST 相反。
Closure.OWNER_ONLY 只在 owner 中寻找,找不到就报错
Closure.DELEGATE_ONLY 只在 delegate 中寻找,找不到就报错
Closure.TO_SELF 在闭包自身中寻找。
集合
在 groovy 中对 Java 的集合做了不少的加强,使得使用起来更加方便。
List
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
def list = [1, 2, 3, 4, 5, 6]
def list1 = [1, "a", true]
println "the list is: ${list}"
println "ths list1 is: ${list1}"
// 默认是 ArrayList
println "the list class is: ${list.class}"
// 往 list 中添加元素
list << 7
println "the list after add is: ${list}"
// 获取 list 中某个区间
println "the list[0,2] is: ${list[0..2]}"
// 获取 list 中指定位置的值
println "the list[0,2] is: ${list[0,2]}"
|
输出结果如下
1
2
3
4
5
6
|
the list is: [1, 2, 3, 4, 5, 6]
ths list1 is: [1, a, true]
the list class is: class java.util.ArrayList
the list after add is: [1, 2, 3, 4, 5, 6, 7]
the list[0,2] is: [1, 2, 3]
the list[0,2] is: [1, 3]
|
其中 << 是添加元素的语法糖,重载了左移操作符。其他和 ArrayList 的操作类似。
还要一个要注意的是,在 List 中可以添加任意类型的元素。
Map
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
def map = [a: 1, b: 2, c: 3]
println "the a in map is: ${map['a']}"
println "the a in map is: ${map.a}"
// 添加元素
map['d'] = 4
println "after add map is: ${map}"
// 遍历
map.each { key, value ->
println "${key}:${value}"
}
map.eachWithIndex { key, value, index ->
println "the $index is $key : $value"
}
|
输出结果如下
1
2
3
4
5
6
7
8
9
10
11
|
the a in map is: 1
the a in map is: 1
after add map is: [a:1, b:2, c:3, d:4]
a:1
b:2
c:3
d:4
the 0 is a : 1
the 1 is b : 2
the 2 is c : 3
the 3 is d : 4
|
Map 中主要的变化就是使用 : 来区分键值对。
文件操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
def file = new File('./test.groovy')
// 写文件
file.createNewFile()
file.withWriter { writer ->
writer.append("println 'Hello Groovy'")
}
// 读文件
file.eachLine { line ->
println line
}
println file.getText()
// 获取每一行,返回是个数组
println file.readLines()
println file.withReader { reader ->
char[] buffer = new char[100]
reader.read(buffer)
return buffer
}
|
输出结果如下
1
2
3
4
|
println 'Hello Groovy'
println 'Hello Groovy'
[println 'Hello Groovy']
println 'Hello Groovy'
|
在 groovy 中操作文件比在 Java 中方便许多。
参考