1 概述
Groovy是一种运行于Java虚拟机的语言。当编译执行Groovy代码时,Groovy代码会被先编译成Java字节码,然后再在Java虚拟机中执行。
2 基本语法
注释
Groovy支持单行注释(//
)和多行注释(/*内容*/
)。
Groovy语句末尾不用加分号。
定义变量
Groovy支持动态类型,即定义变量的时候可以不指定其类型。Groovy中,定义变量使用关键字def(def关键字也可以省略)。
def variable1 = 1 //不指定类型,使用def定义变量
def variable2 = "I am a person"
variable3 = "hello" //不指定类型,也不写def
byte b = 1 //变量定义时,也可以直接指定类型
char c = 2
short s = 3
int i = 4
long l = 5
float f = 1.234
double d = 2.345
boolean untypedBooleanVar = false
定义、调用函数
定义函数时,可以不指定参数的类型:
String testFunction(arg1,arg2){//无需指定参数类型
...
}
也可以不指定函数的返回值类型。但若不指定函数的返回值类型,则必须加上def关键字:
String getString(){
"I am a string"//最后一行代码的值就是本函数的返回值
}
//不指定返回值类型,则必须使用def关键字
def nonReturnTypeFunc(){
"I am a string"//最后一行代码的值就是本函数的返回值
}
Groovy的函数里,可以不使用 return xxx 来设置 xxx 为函数返回值。如果不使用 return 语句的话,则函数中最后一行代码的执行结果被设置成返回值。
Groovy中调用函数时可以省略小括号,如println("hello")
和println "hello"
是等价的。
字符串
- 单引号字符串:单引号字符串等价于Java中的字符串。不支持$表达式。
def age = 20
def str = 'I am $age years old'
println str //打印结果为:I am $age years old
- 双引号字符串:双引号字符串支持
表达式有两种格式,一种是
name
;另一种是$后跟一个花括号,花括号中可以是一个复杂的表达式,如
{2+6}`等。
def age = 20
def str = "I am $age years old"
println str //打印结果为:I am 20 years old
- 三单引号字符串:三单引号字符串支持跨行。不支持转义,任何字符都会被原样展示(包括换行、空格、tab等)。
def str = '''line one
line two
line three
'''
3 容器类
3.1 List
使用[]来进行定义:
def aList = [5,'string',true] //元素的类型可以不同
通过index访问元素,不用担心index越界:
assert aList[1] == 'string'
assert aList[5] == null //第6个元素为空
aList[100] = 100 //设置第101个元素的值为10
println aList.size //结果是101
3.2 Map
定义:
//冒号左边是key,右边是value。key必须是字符串,value可以是任何类型。key可以用''或""包起来,也可以不用引号包起来。
def aMap = ['key1':'value1','key2':true]
访问:
println aMap.key1
或
println aMap['key1']
3.3 Range
//包含1,2,3,4,5这5个值
def aRange = 1..5
//只包含1,2,3,4这4个元素
def aRangeWithoutEnd = 1..<5
//迭代
def sum = 0
aRange.each{element->
sum+=element
}
4 闭包
闭包(Closure)是Java所不具备的语法结构(JAVA8增加了对闭包的支持)。闭包就是一个代码块,用“{ }”包起来。此时,程序代码也就成了数据,可以被一个变量所引用。
闭包的创建过程很简单,例如:
{ 参数 ->
代码...
}
参考下面的示例代码,定义了c1和c2两个闭包,并对它们进行调用:
定义闭包:
def c1 = { text -> println text }
def c2 = { println it }//如果闭包只有一个参数,则参数和->都可以不写,在后面的代码中以it代表调用时传入的该参数
调用闭包:
c1.call("content1") //方式1,用call方法调用闭包
c2("content2") //方式2,直接调用闭包
闭包的返回值和函数的返回值定义方式是一样的:如果有return语句,则返回值是return语句后面的内容;如果没有return语句,则闭包内的最后一行代码就是它的返回值。
类比c语言,指向闭包的变量就像是一个函数指针,而闭包就是一个函数。
省略参数圆括号
闭包在Groovy中大量使用,比如很多函数的最后一个参数都是一个闭包。比如:
def debugClosure(int num, String str, Closure closure){
//dosomething
}
//调用函数
debugClosure(1, "groovy", {
println"hello groovy!"
})
当闭包作为函数的最后一个参数时,我们可以将闭包从参数圆括号中提取出来接在最后:
debugClosure(1, "groovy"){
println"hello groovy!"
}
也可以直接省略圆括号:
debugClosure 1, "groovy", {
println"hello groovy!"
}
由以上知识可知,在build.gradle文件中,形如:
defaultConfig {
applicationId "com.dmall.pop"
minSdkVersion 15
targetSdkVersion 22
versionCode 4
versionName "2.1.0"
vectorDrawables.useSupportLibrary = true
}
或
variant.outputs.each { output ->
def oldFile = output.outputFile
def gitTag = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim()
if (variant.buildType.name.equals('release')) {
def releaseApkName = 'POP_' + variant.productFlavors[0].name + '_v' + defaultConfig.versionName + '_' + gitTag + '.apk'
output.outputFile = new File(oldFile.parent, releaseApkName)
}
}
其实都是一个方法调用,方法名是defaultConfig和each,方法名后跟着的闭包是传递给方法的参数(省略了参数外面的小括号)。
一个闭包可以理解为一个方法,将一个闭包作为参数传递给另一个方法,其实类似于c语言中的将函数指针作为参数传递给另一个函数。类比android,这里闭包其实很像android的liseter,我们将一个闭包传递过去,在未来的某个时间,这个闭包就会被调用。
5 将一般的函数当做闭包使用
Groovy支持.&
函数指针运算符,因为闭包可以被作为一个函数的参数,如果想让一个函数作为另一个函数的参数,则可以通过.&
运算符将一个函数当成一个闭包作为另一个函数的参数。
def list = ['a','b','c']
//常规写法
list.each{
println it
}
String printName(name){
println name
}
//方法指针操作符写法
list.each(this.&printName)
6 变量的作用域
在groovy文件(xxx.groovy)中可以像在java文件中那样定义一个类,也可以直接写脚本(即直接写要执行的操作),例如test.groovy的代码是:
println 'Groovy world!
它会被转换成这样的Java类:
- test.groovy被转换成了一个test类,它继承自Script。
- Java类中会有一个main函数,当我们执行脚本的时候,实际就是去执行main函数。
- 脚本中的所有代码都会放到run函数中。也就是说,
println 'Groovy world'
这句代码实际上是包含在run函数里的。 - 脚本中定义的函数,会被转换成test类的成员函数。
- 脚本中以
def 变量名
或类型 变量名
格式定义的变量,会被转换为run方法中的局部变量。
再来看另一个脚本文件:
def x = 1 //注意,这个x有def(或者指明类型,比如 int x = 1)
def printx(){
println x
}
printx() //报错,说 x 找不到
报错的原因很明显——成员函数printx无法访问到run函数中的局部变量x。
那么要想在printx函数中访问变量x,该如何做呢?——只要去掉x前的def或者类型即可,即写成x = 1
。此时的Java代码如下图所示:
可见,x仍没有被定义成test的成员变量,而是在run的执行过程中,将x作为一个属性添加到test实例对象中了,然后在printx函数中,先获取这个属性。
虽然printx可以访问变量x了,但是在其他脚本文件中,依然无法访问变量x,例如我们在test.groovy同目录下创建了一个test1.groovy:
def atest = new test()
atest.printx()
提示找不到x,这是因为x是在test.groovy的run函数中动态地添加到test实例中去的。那该怎么办呢?只要给x加上@Field
注解:
import groovy.transform.Field;
@Field x = 1 //在x前面加上@Field注解,这样,x就是test的成员变量了
7 文件I/O
相关API文档:
类 | API |
---|---|
java.io.File | http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html |
java.io.InputStream | http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html |
java.io.OutputStream | http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/OutputStream.html |
java.io.Reader | http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Reader.html |
java.io.Writer | http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Writer.html |
java.nio.file.Path | http://docs.groovy-lang.org/latest/html/groovy-jdk/java/nio/file/Path.html |
7.1 读文件
def targetFile = new File(文件名)
//读取文件的每一行并打印
targetFile.eachLine{
String oneLine ->
println oneLine
}
//一次性读取文件内容,得到一个bytes[]
targetFile.getBytes()
//获得读入流
def is = targetFile.newInputStream()
//关闭流
is.close
//使用闭包操作inputStream,常用。
targetFile.withInputStream{ is ->
//操作is。不用close。Groovy会自动替你close
}
7.2 写文件
举个copy文件的例子:
def srcFile = new File(源文件名)
def targetFile = new File(目标文件名)
targetFile.withOutputStream{ os->
srcFile.withInputStream{ is->
//利用 OutputStream 的<<操作符重载,完成从 inputstream 到 OutputStream的输出
os << is
}
}
7.3 解析XML
test.xml文件:
<response version-api="2.0">
<value>
<books>
<book available="20" id="1">
<title>Don Xijote</title>
<author id="1">Manuel De Cervantes</author>
</book>
<book available="14" id="2">
<title>Catcher in the Rye</title>
<author id="2">JD Salinger</author>
</book>
<book available="13" id="3">
<title>Alice in Wonderland</title>
<author id="3">Lewis Carroll</author>
</book>
<book available="5" id="4">
<title>Don Xijote</title>
<author id="4">Manuel De Cervantes</author>
</book>
</books>
</value>
</response>
解析:
def xparser = new XmlSlurper()
def targetFile = new File("test.xml")
GPathResult gpathResult =xparser.parse(targetFile)
//访问id=4的book元素
//gpathResult代表根元素response。通过e1.e2.e3这种格式就能访问到各级子元素....
def book4 = gpathResult.value.books.book[3]
//得到book4的author元素
def author = book4.author
//获取元素中的文本
assert author.text() == 'Manuel De Cervantes'
//获取属性。格式:author.@属性名 或 author['@属性名']。属性一般是字符串,可通过toInteger转换成整数
[email protected]() == 4
8 DSL
DSL即领域特定语言。Groovy是通用的编程语言,所以不是DSL,但是使用Groovy可以很容易实现DSL,这主要是因为Groovy语言的一些特性,如:
- Groovy源文件中不需要定义Class,可以直接写代码;
- Groovy调用函数时可以省略函数参数的小括号;
- 等。
9 参考
深入理解Android之Gradle:http://blog.csdn.net/innost/article/details/48228651
Groovy脚本基础全攻略 :http://blog.csdn.net/yanbober/article/details/49047515