Gradle是目前Android主流的构建工具,不管你是通过命令行还是通过Android Studio来build,最终都是通过Gradle来实现的,所以学习Gradle非常重要。
Groovy最终被编译成class字节码文件运行在jvm上,所以Java的特性Groovy都支持,但是Groovy提供了一些更加简洁的语法,比如利用闭包特性来进行文件IO,解析XML,简直让你难以置信。
一.开发环境准备
- 方式一:安装Groovy SDK,安装方法参考 http://www.groovy-lang.org/download.html,下载后解压即可,最好是设置一下环境变量。
- 方式二:直接在Android Studio中开发,创建一个普通的Android Project,在build.gradle文件中编写一个task来执行,示例:
task(jdqm).doLast {
println "Start execute hello."
hello()
}
def hello() {
println "Hello jdqm."
//todo add your code here
}
在hello方法中编写你的Groovy代码,然后通过如下命令来执行
gradlew jdqm
作为Android开发者,个人比较推荐是用第二种方式,毕竟我们的目标是Gradle。
二. 开发实战
Groovy中的注释与Java中的一样,单行注释//,多行注释/**/
1.def关键字定义变量
def int aInt = 1;
def String aString = "I am a String.";
- 分号不是必须的
- def关键字可省略
- 数据类型自动推断
上面的代码可以写成这样
aInt = 1
aString = "I am a String."
bInt = 2
2.方法的参数类型和返回值类型可省略
def getSomeThing(param1) {
return param1
}
println getSomeThing("Hello Jdqm.")
请注意 println getSomeThing("Hello Jdqm.") 这一行,实际是调用了 println(String)这个方法,但你会发现没有圆括号,所以方法调用圆括号也不是是必须的,前提是不引起混淆。下面这两种写法效果是一样的:
println getSomeThing("Hello Jdqm.")
println(getSomeThing("Hello Jdqm."))
那什么时候会混淆,比如
//方法调用省略圆括号,容易引起混淆,getSomeThing 会被当成属性
println getSomeThing "Hello Jdqm."
3.return关键字可省略
//return关键字可省略,返回值为最后一条语句的执行结果
def getSomeThings(param1, param2) {
param1 + param2
}
result = getSomeThings 3, 4
println result // 7
注意:方法的返回值类型和def关键字不能同时省略,否则会报找不到方法。
4.字符串
//单引号为严格意义的字符串,输出 I am $ dollar
String singleQuote(){
return 'I am $ dollar'
}
println singleQuote() // I am $ dollar
//双引号不是严格的字符串,如果其中有$表达式,会先对表达式求值
String doubleQuote(){
def x = 1
"I am $x dollar"
}
println doubleQuote() // I am 1 dollar
//三引号可以随意换行
String threeQuote(){
'''line1
line2
line3'''
}
5.自动类型推断
//自动推断类型
def x = 1
println x.getClass().getCanonicalName() //java.lang.Integer
6.加强版 List
List可以存放任何类型的数据类型
List emptyList = [] // 通过[]来创建List
List testList=[100, 'hello', true]
println(testList[0]) //100
println(testList[1]) //hello
println(testList[2]) // true
assert testList[0]==100 //true
assert testList[100]==null / /true
//不会有下标越界异常,自动扩展List的大小
testList[100]=10
println testList[100] //10
println testList.size() //101,注意这个List的大小已经变为101,那么中间的元素默认值是什么是什么?是null
是不是特别像Java中的List<Object>
7.Map
通过[:]来创建,key默认是String,可以不加引号,但是容易引起混淆,单引号双引号都可以。
Map emptyMap = [:]
Map testMap = ['key1':'value1','key2':'value2']
println(testMap.key1) //value1
println(testMap['key2']) //传统的取值方式
testMap.anotherkey='anothervalue' //添加一个元素
println testMap.anotherkey //anothervalue
8.Range
def aRange=1..5 //1 2 3 4 5
def bRange=1..<5 //1 2 3 4
println aRange.from //1
println aRange.to //5
println bRange.from //1
println bRange.to //4
其中 aRange.from这种调用方式,实际上是调用了aRange.getFrom()方法。
9.闭包
闭包是一个比较重要的内容。
def aClosure = {String param1, String param2->
println param1
println param2
}
//有两种调用方式
aClosure.call('hello', 'groovy')
aClosure('hello','groovy')
定义无参数closure,不代表不能传参数
def bClosure={
println 'hello world of groovy.'
println it // 没有定义参数的时候,有一个隐含参数it
}
bClosure.call() //不传参数调用,it为null
bClosure(100) //it为100
下面这种写法表示闭包没有参数,调用时不能传入参数,否则会报错
def cClosure={->
println 'hello world.'
}
//不传入参数调用,正确
cClosure.call()
//这样调用会报错
//cClosure.call('test')
方法的最后一个参数是闭包,可以将这个闭包参数写到圆括号外
def testClosure(int a, String param, Closure closure){
println ("$a,$param")
closure.call()
}
//传统的调用方式
testClosure(10, 'groovy',{
println('I am a Closure')
})
//最后一个参数写到圆括号外
testClosure(10, 'groovy') {
println('I am a Closure')
}
10. 文件IO
首先回顾一下在Java中进行文件IO,假设存在这样的test.txt
hello
groovy
I
am
Jdqm.
现在需要通过Java IO读这个文件的内容并输出到控制台
BufferedReader bufferedReader = null;
try {
File file = new File("test.txt");
//文本内容可以使用字符流
FileReader fileReader = new FileReader(file);
//将字符流包装为BufferReader,方便按行读取
bufferedReader = new BufferedReader(fileReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
即便使用Java7中的自动关闭流来省略掉finally块,代码仍然比较繁琐。接下来使用Groovy来完成同样的功能。
File file = new File('test.txt')
file.eachLine {online->
println online
}
到这里我只想到两个词:简洁、明了。这里使用到了闭包,而闭包的难点是如何确定参数,最好的方式是查官方文档。http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html#eachLine(groovy.lang.Closure)
从文档说明中我们得知调用时闭包可以是1个或者2个参数, 第一个参数是当前行的内容,第二个参数(可选)是当前的行号(从1开始)。所以我们可以这样
File file = new File('test.txt')
file.eachLine{line, lineNum ->
println lineNum + " " + line
}
//输出结果
1 hello
2 groovy
3 I
4 am
5 Jdqm.
下面将test.txt的内容写入test1.txt
File sourceFile = new File('test.txt')
File targetFile = new File('test1.txt')
targetFile.withOutputStream{os->
sourceFile.withInputStream{is->
os<<is
}
}
11. 解析XML
Groovy访问xml有两个类:XmlParser和XmlSlurper,二者几乎一样,在性能上有细微的差别,参考链接 http://docs.groovy-lang.org/docs/latest/html/api/。
def xml = '<root><one a1="uno!"/><two>Some text!</two></root>'
def rootNode = new XmlParser().parseText(xml)
assert rootNode.name() == 'root'
assert rootNode.one[0].@a1 == 'uno!'
assert rootNode.two.text() == 'Some text!'
rootNode.children().each { assert it.name() in ['one','two'] }
12.其他
在Groovy中,Getter/Setter和属性是默认关联的,比如:
class Book {
private String name
String getName() { return name }
void setName(String name) { this.name = name }
}
class Book {
String name
}
上述两个类完全一致,只有有属性就有Getter/Setter;同理,只要有Getter/Setter,那么它就有隐含属性。
with操作符
在Groovy中,当对同一个对象进行操作时,可以使用with,比如:
Book bk = new Book()
bk.id = 1
bk.name = "android art"
bk.press = "china press"
可以简写为:
Book bk = new Book()
bk.with {
id = 1
name = "android art"
press = "china press"
}
判断是否为真
在Groovy中,判断是否为真可以更简洁:
if (name != null && name.length > 0) {}
可以替换为:
if (name) {}
简洁的三元表达式
在Groovy中,三元表达式可以更加简洁,比如:
def result = name != null ? name : "Unknown"
// 省略了name
def result = name ?: "Unknown"
简洁的非空判断
在Groovy中,非空判断可以用?表达式,比如:
if (order != null) {
if (order.getCustomer() != null) {
if (order.getCustomer().getAddress() != null) {
System.out.println(order.getCustomer().getAddress());
}
}
}
可以简写为:
println order?.customer?.address
使用断言
在Groovy中,可以使用assert来设置断言,当断言的条件为false时,程序将会抛出异常:
def check(String name) {
// name non-null and non-empty according to Gro ovy Truth
assert name
// safe navigation + Groovy Truth to check
assert name?.size() > 3
}
switch方法
在Groovy中,switch方法变得更加灵活,可以同时支持更多的参数类型:
def x = 1.23
def result = ""
switch (x) {
case "foo": result = "found foo"
// lets fall through
case "bar": result += "bar"
case [4, 5, 6, 'inList']: result = "list"
break
case 12..30: result = "range"
break
case Integer: result = "integer"
break
case Number: result = "number"
break
case { it > 3 }: result = "number > 3"
break
default: result = "default"
}
assert result == "number"
==和equals
在Groovy中,==相当于Java的equals,,如果需要比较对个对象是否是同一个,需要使用.is()。
Object a = new Object()
Object b = a.clone()
assert a == b
assert !a.is(b)