【gradle使用前篇—Groovy简介】
Groovy介绍
Groovy是一种动态语言,对它的定义是:Groovy是在java平台上的,具有像Python、Ruby和smalltalk语言特性的灵活动态语言,Groovy保证了这些特性像java语言一样被java开发者使用。说白了就是让写java程序变的像写脚本一样简单,Groovy内部会将其编译成java class,然后放到JVM上执行。
此外Groovy是一种DSL(Domain Specific Language),是针对某个领域所涉及出来的一个特定的语言,因为有了领域的限制,要解决的问题就被划定了范围,所以语言不需要复杂,就可以具有精确的表达能力,使用起来非常方便。更具体的来说:使用java或者C++也能够实现相同的功能,但是会产生大量繁琐的代码并导致大量的领域知识被隐藏在通用语言构造中(比如:for循环、if条件等等)。(关于DSL感兴趣的,可以自己Google,可以深入学习下怎么定义自己的DSL语言)。
1 语法基础
1)注释
Groovy的单行注释、多行注释、文档注释和java一样。只有一种特殊的单行注释需要留意:
#!/usr/bin/env groovy
println "Hello from the shebang line"
这种注释在脚本中经常见到,一般都是固定写法。
2)关键字
Groovy有如下的关键字:
as、assert、break、case、catch、class、const、continue、def、default、do、else、enum、extends、false、finally、for、goto、if、implements、import、in、instanceof、interface、new、null、package、return、super、switch、this、throw、throws、trait、true、try、while。
看到了吧,其实它和java的关键字区别不大,其中不一样的关键字是:
- def用来定义一个变量,定义变量时可以不指定类型。
- goto在java中是保留关键字
- assert常用在C或者C++中
- trait是一组可重用的方法和字段,可以将他们混入一个或者多个类中,一个类可以同时拥有多个trait而不需要使用多重继承。
- 其他关键字可能有的略有不同,在使用过程中可以再了解。
3)标识符
- 普通标识符和常用语言类似,只能以字母、美元符号、下划线开始,不能以数字开始
- 引用标识符出现在点后的表达式中,比如:
def map = [:] //引用标示符中出现空格也是对的 map."an identifier with a space and double quotes" = "ALLOWED" //引用标示符中出现横线也是对的 map.'with-dash-signs-and-single-quotes' = "ALLOWED" assert map."an identifier with a space and double quotes" == "ALLOWED" assert map.'with-dash-signs-and-single-quotes' == "ALLOWED"
Groovy的所有字符串都可以当作引用标识符定义,如下:
//如下类型字符串作为引用标识符都是对的 map.'single quote' map."double quote" map.'''triple single quote''' //三个引号的,可以换行显示 map."""triple double quote""" map./slashy string/ map.$/dollar slashy string/$ //稍微特殊的GString,也是对的 def firstname = "Homer" map."Simson-${firstname}" = "Homer Simson" assert map.'Simson-Homer' == "Homer Simson"
4)字符及字符串
- 单引号字符串是String类型的,不支持占位符,比如
def name = 'Test Groovy!' def body = 'Test $name' assert name == 'Test Groovy!' assert body == 'Test $name' //不会替换$name站位符
字符串可以通过 + 直接拼接。
- 三重单引号字符串是String类型的,不支持占位符插值操作,可以标识多行字符串,比如:
def aMultilineString = '''line one
line two
line three'''
- 双引号字符串支持占位插值操作,如果双引号字符串不包含占位符则是String类型的,如果双引号字符中包含占位符则是GString类型的。对于插值占位符我们可以使用${}或者$表示,${}用于一般替代字符串或者表达式,$主要用于A.B的形式中,比如:
def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}"
assert greeting.toString() == 'Hello Guillaume'
def sum = "The sum of 2 and 3 equals ${2 + 3}"
assert sum.toString() == 'The sum of 2 and 3 equals 5'
def person = [name: 'Guillaume', age: 36]
assert "$person.name is $person.age years old" == 'Guillaume is 36 years old'
//注意的是:$只对A.B等有效,如果表达式包含括号、大括号、闭包等符号则是无效的。比如:
def number = 3.14 shouldFail(MissingPropertyException) { println "$number.toString()" } //该代码运行抛出groovy.lang.MissingPropertyException异常,因为Groovy认为去寻找number的名为toString的属性,所以异常
- 还有一个很NB的结论,一个普通插值表达式替换实际是在GString创建的时刻,一个包含闭包的表达式由于延迟运算调用toString()方法,所以会产生一个新的字符串值。
def number = 1 def eagerGString = "value == ${number}" def lazyGString = "value == ${ -> number }" assert eagerGString == "value == 1" assert lazyGString == "value == 1"
// 当number = 2 时,两者出现了不同 number = 2 assert eagerGString == "value == 1" assert lazyGString == "value == 2"
- GString和String的hashCode是不一样的。
- 多重双引号字符串也支持占位插值操作。
- 斜线字符串和双引号字符串类似,通常用在正则表达式中。
//普通使用 def fooPattern = /.*foo.*/ assert fooPattern == '.*foo.*' //含转义字符使用 def escapeSlash = /The character \/ is a forward slash/ assert escapeSlash == 'The character / is a forward slash' //多行支持 def multilineSlashy = /one two three/ assert multilineSlashy.contains('\n') //含站位符使用支持 def color = 'blue' def interpolatedSlashy = /a ${color} car/ assert interpolatedSlashy == 'a blue car'
- Groovy没有明确的Characters,但是我们可以用如下三种方式处理字符
char c1 = 'A' assert c1 instanceof Character def c2 = 'B' as char assert c2 instanceof Character def c3 = (char)'C' assert c3 instanceof Character
5)Lists类型
Groovy支持List类型,可以增加或者删除对象,列表中的对象不受类型限制,比较特殊的是可以通过超出列表范围的数字来索引列表。可以做个小例子:
//和java中的思想是一样的 def numbers=[1,2,3]; assert numbers instanceof List assert numbers.size() == 3 //存储任意类型 def test = [1, 'a', true] //使用as强制类型转换 def linkedList = [2,3,4] as LinkedList assert linkedList instanceof java.util.LinkedList //给List追加item def letters = ['a', 'b','c','c'] letters << 'e' assert letters[4] == 'e' //多维List支持 def multi = [[0, 1], [2, 3]] assert multi[1][0] == 2
6)Map类型
Map是“键-值”对的集合,在Groovy中键key不一定是String,可以是任意对象。
def colors=[red:'1',green:'2', blue:'3'] assert colors.red == '1' //对于map需要特别注意一种情况,如下: //把一个定义的变量作为Map的key的正确写法---添加括弧,访问Map的该key是成功的 def key = 'name' def person=[(key):'liwei'] assert !person.containsKey('key') assert person.containsKey('name')
7)Range类
Range是Groovy对List的一种拓展:
def aRange = 1..5 <==Range类型的变量 由begin值+两个点+end值表示 左边这个aRange包含1,2,3,4,5这5个值 如果不想包含最后一个元素,则 def aRangeWithoutEnd = 1..<5 <==包含1,2,3,4这4个元素 println aRange.from println aRange.to
2 闭包
1)闭包(Closure)的定义
闭包是Groovy中非常重要的一个数据类型或者说一个概念。它代表了一段可执行的代码,其外形如下:
def aClosure = {//闭包是一段代码,所以需要用花括号括起来.. String param1, int param2 -> //这个箭头很关键。箭头前面是参数定义,箭头后面是代码 println"this is code" //这是代码,最后一句是返回值, //也可以使用return,和Groovy中普通函数一样 }
//简而言之,闭包的定义格式是:
def xxx={paramters ——> code}
def xxx={无参数,纯code} 这种case不需要 ——>符号
//调用闭包的方式:
xxx.call(paramter1,paramter2...)
xxx(paramter1,paramter2....)
//如果闭包没有定义参数的话,则隐含有一个参数,这个参数交 it, 和java中this类似。
def xxx = {“Hello,$it!”}
assert xxx('Groovy') == 'Hello Groovy!'
//不过如果想定义的本来就是没有参数,则可以定义如下
def noParamClosure = {—> true}
2)使用中注意的点
- 省略圆括号
//闭包在Groovy中大量使用,比如下面这个函数,最后一个参数是闭包
public static <T> List<T> each(List<T> self, Closure closure)
//上面这个方法是List中的,表示可以对List中每一个元素在闭包中做一些处理,我们看下如何使用它:
def iamList = [1,2,3,4,5] //定义一个List iamList.each{ //调用它的each,这段代码的格式看不懂了吧?each是个函数,圆括号去哪了? println it }
//这里有有两个知识点:
1)Groovy中,当函数的最后一个参数是闭包的话,可以省略圆括号。
2)如何确定Closure参数
我们还以上面的那个iamList为例,通过查看API可以知道 【递归set中的元素,传递每一个item给闭包】
所以参数其实就明确了,是List中的每一个元素。
同理我们也可以得出,对于Map来说,它的类似方法中的闭包可以有两个参数,以findAll为例,会将
key和Value分别传进去。 从这里我们可以看出,一定要对API熟悉,才能很好的使用闭包。
//我们在平时的gradle项目中也常常见到
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
3 脚本类、文件IO和XML操作
1)脚本类
- 脚本中import其他类
在Groovy中,系统自带会加载当前目录及其子目录下的xxx.groovy文件。
- 脚本到底是什么
当通过 groovy xxx.groovy运行脚本时,Groovy会先把xxx.groovy中的内容转换成一个java类(groovyc -d classes test.groovy)。这个java类有以下特点:
1. xxx.groovy被转换成一个test类,继承script
2. 每一个脚本都会生成一个static main 函数,所以当执行 groovy test.groovy时,其实就是执行了main方法
3. 脚本中所有代码都会放到run方法中。
4. 如果脚本中定义了函数,则函数会被定义在test类中。
- 脚本中的变量和作用域
def x = 1 <==注意,这个x有def(或者指明类型,比如 int x = 1) def printx(){ println x //这里会报错,提示x找不到 }
//其实从上面的介绍已经可以知道为啥这里为啥报错了。
原因就是:printx方法在类中是一个成员函数,但是x会被放到 run方法中,所以printx方法是不可能访问到x元素的。
2)文件I/O操作
- 读文件
def targetFile=new File(文件名) //创建File对象
targetFile.eachLine{ //读文件中的每一行
String oneLine ->
println oneLine
}
targetFile.getBytes() //文件一次性读出,返回类型为byte【】
def ism = targetFile.newInputStream() //流
ism.close
targetFile.withInputStream{
ism ->
不需要close, Groovy会自动替你close
}
- 写文件
def srcFile = new File(源文件名) def targetFile = new File(目标文件名) targetFile.withOutputStream{ os-> srcFile.withInputStream{ ins-> os << ins //利用OutputStream的<<操作符重载,完成从inputstream到OutputStream //的输出 } }
3)XML操作
Groovy提供了一个GPath。直接上例子看下怎么用
<!--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>
//第一步,创建XmlSlurper类 def xparser = new XmlSlurper() def targetFile = new File("test.xml") //轰轰的GPath出场 GPathResult gpathResult =xparser.parse(targetFile) //开始玩test.xml。现在我要访问id=4的book元素。 //下面这种搞法,gpathResult代表根元素response。通过e1.e2.e3这种 //格式就能访问到各级子元素.... def book4 = gpathResult.value.books.book[3] //得到book4的author元素 def author = book4.author //再来获取元素的属性和textvalue assert author.text() == ' Manuel De Cervantes ' 获取属性更直观 author.@id == '4' 或者 author['@id'] == '4' 属性一般是字符串,可通过toInteger转换成整数 author.@id.toInteger() == 4 好了。GPath就说到这。再看个例子。我在使用Gradle的时候有个需求,就是获取AndroidManifest.xml版本号(versionName)。有了GPath,一行代码搞定,请看: def androidManifest = newXmlSlurper().parse("AndroidManifest.xml") println androidManifest['@android:versionName'] 或者 println androidManifest.@'android:versionName'
4 总结
Groovy是一门语言,因此一篇文章不可能表达的很清楚,只是作为入门教程,对Groovy有个大概的认识,为之后学习Gradle打基础,如果想更深入的了解Groovy,需要自行的实践和Google了。
参考博客:
http://www.infoq.com/cn/articles/android-in-depth-gradle/
api文档
http://tool.oschina.net/apidocs/apidoc?api=groovy