前言

成为一名优秀的Android开发,需求一份完备的 常识体系,在这儿,让咱们一同生长为自己所想的那样~。

Groovy 作为 Gradle 这一强壮构建工具的中心言语,其重要性不言而喻,可是 Groovy 自身是十分复杂的,要想全面地把握它,我想几十篇万字长文也无` D u – | S ? P E法将其完全描述。所幸的是,在 Gradle 范畴V & 4 = c 2 @ R中触及的 Groovy 常s P E识都是非常根底的,因而,本篇文章的意图是为了在后续深化探究 Gradle 时做好必定的根底储备。

一、DSL 初识

DSL(domain specific language),即范畴特定言语,例如:Matliba、UML、HTML、XML 等等 DSL 言语。能够这样了解,Groovy 便是 DSL 的一个分支。

特色

  • 1)、处理特定范畴的专有问题。
  • 2)、它与体系编程言语走的是两个极点,体系编程言语是期望处理一切的问题,比方 Java 言语期望能做k 0 j 1 y Android 开发,又期望能做服务器开发,它具有横向扩展的特性。而 DSL 具有纵向深化, 9 I v B c处理特定范畴专有问题的特性。

总的来说,DSL 的 中心思维 便是:“求专L z R不求全,处理特定范畴的问题”

二、Groovy 初识

1、Groovy 的特色

Groovy 的特色具有如下 三点:

  • 1)、Groovy 是一种依据 JVM 的灵敏开发言语。
  • 2)、Groovy 结合了 Python、Ruby 和 Smalltalk 众多脚本言语的许多强壮的特性。
  • 3)、GrR h S S 2oovy 能够与 Java 完美结合,而且能够运用 Javas V K R g 一切的c / b y ; 1 ,库。

那么,在现已有了其它脚本言语的前2 e 2 { J [ 9 z提下,为什么还要制造出 Grvooy 言语呢?

因为 Groovy 言语相较其它编程言语而言,其 入门的学习成本是5 H k D / Z C ; R非常低的,因为它的语法便是对 Java 的扩展,所以,咱们能够用学习 Java 的办法去学习N ^ b J M 7 Groovy。

2、Groov` 0 – N # Ky 言语自身的特性

其特性首要有如下 三种:

  • 1y $ | b K ~ s g)、语法上支撑动态类型,闭包等新一代言语特性。而且,Groovy 言语的闭包比其它一切言语类型的闭包都要强壮。
  • 2)、它能够无缝集成一切现已存在的 Java 类库,因为它n 3 R _ @ ; +是依据 JK Q : P ~ 7 _VM} m s N C ) E 的。
  • 3)、它即能够支撑面向方针编程(依据 Java 的扩展),也能够支撑面向进程编程(依据众多脚本言语的结合)。

需求留意的是,在咱们运用 Groovy 进行I f i Gradle 脚本编写的时分,都是运用的面向进程进行编程的

3、Groovy 的优势

Groovy 的优势有如下 四种:

  • 1)、它是一种愈加灵敏的编程言语:在语法上构建除了非常多的语法糖x X A,许多在 Java 层需求写的代码,在 Groovy 中是能够省掉的。因而,咱们能够用更少的代码完结更多的功用。
  • 2)、入门简单,但功用非常强壮。
  • 3)、既能够作为编程言语也能够作为脚本言语
  • 4)、/ Q c熟悉把握 Java 的同学会非常容易把握 Groovy。

4、Groovy 包的Q ~ I k ~结构

Groovy 官方网址

从官网下载好 Groovy 文件之后,咱N G G w [ N S们就能够看– M + S $ 4 * 3 _Groovy 的目录结构,其间咱们需求 要点重视 bin 和 doc 这个两个文件夹

bin 文件夹

bin 文件夹的内容如下所示:

这儿咱们了解下三个重要的可履行指令文件,如下所示:

  • 1)、, H A p &grr { ; ioovy 指令类似于 Java 中的 java 指令,用~ m L k q ]于履行 groovy Class 字节; q 5 m ; ( + #码文件。
  • 2)、groovyc 指令类似于 Java 中的 javac 指令,用于将 groovy 源文件编译成 groovy 字节码文件。
  • 3)、groz 8 *ovysh 指令是用来解说履行 groovy 脚本文件的。

doc 文件夹

doc 文件夹的下面有一个 html 文件,其间的内容如下所示:

这儿的 api 和 documentation 是咱们需求要点重视的,其效果别离如下所示:

  • api:gs 0 z 2 P uroovy 中为咱们供给的一系列 API 及其 阐明文档。
  • documentation:grn H k + Ooovy 官方为咱们供给的一些教程。

5、Groovy 中的要害字

下面是 Groovy 中一切的要害字,命名时特别需求留意,如下所示:

asassertbreakcasecatchclassconstcontinuedefdefault
doelseenum! c ^ m qexE 3 a ^tendsfalsefinallyforN ] + ; j 9 _gotoifimplements
importinS O g Yinstanceofinterfacenewnullpackagereturnsuper
switchthisthrowthrowstraittrueta X -rywhile

6、Groovy && Java 差异学习z N o d

1)、getter / setter

对于每一个 field,Groovy 都会⾃动创立其与之对应的 getter 与 setter 办法,从外部能够直接调用它,而且 在使⽤ object.fieldA 来获取值或许运用 object.fieldA = value 来赋值的时分,实践上会主动转而调⽤ object.getFieldA() 和 object.setFieldA(value) 办法

假如咱们2 R ^ t C n不想调用这个特殊的 getter 办法时则能够运用 .@ 直接域拜访操作符

2)、除了每行代码不必加分号r 6 Q外,Groovy 中函数调用的时分还能够不加括号。

需求留意的是,咱们在运用的时l h $分,假如当时这个k { S q函数L t ; @ 8 D是 Groovy API 或许 Gradle
API 中比较常用的,比方 println,就能够不带括号。否则还是带括号。否则,Groovy 可能会把特点和函数调用混淆

3)、Groovy 语句能够不必分号结束。

4)、函数界说时,参数的类型也能够不指定。

5)、Groovy 中函数的回来值也能够是无类型的,而且无回来类型的函数,其内部都是按回来 Object 类型来处理的。

6)、当时函数假如没有运用 rD . 2 4 Qeturn 要害字回来值: y . t m u 1 P 7,则会默许回来 null,但此刻有必要运用 def 要害字。

7)、在 GI , w ) 7 D ^ L @roovy 中,一切的 Class 类型,都能够省掉 .clS w P h b dass。

8)、在 Groovy 中,==* K x } 9 g 6 相当于 Java 的 equals,,假如需求比较两A ` W *个方针是否是同一个,需求运用 .is()。

9)、Groovy 非运算符如下:

assert(!"android")=q * N 0=false

10)、Groovy 支撑 ** 次方运算符,代码如下所示:

assert2**3==8

11)、判断G N Q *是否C % m w 9为真能够更简练:

if(android){}

12)、三元表达式能i ( , a `够愈加简练:

//省掉了name
defresult=name?:"Unknown"v g { ! V

13)、简练的非空判断

printlnorder?.customer?.address

14)、运用 assert 来设置断言,当断言的条件为 fal3 Q z – } j 1 =se 时,程序将会抛出反常。

15)、能够运用 Number 类去代替 float、double 等类型,省去考虑精度的费事。

16)、switch 办法能够一同支撑更多的参数类型。

留意,swctch 能够匹配列表当中任一元素,示例代码如下所示:

//输出ok
defnum=5.21
switch(num){
casef g e[5.21,4,"list"]:
return"ok"
break
default:
break
}

三、Groovy 根底语法} % { L , v

Groovy 的根底语法首要能够分为以下 四个部分:

  • 1)、Groovy 中心根底语法。
  • 2)、Groovy 闭包。
  • 3)、Groovk b * R t 7y 数据结构。
  • 4)、G4 q d L B 4 |ro@ y 6 7 ] 4 qovy 面向方针

1、G& k J 1 @ 9 ? .roovy 中心O z i 3 )根底语法

Groovy 中的变量

变量类型

GroovC 6 C ) N | d r Oy 中的类型同 Java 相同,也是分为如下 两种:

  • 1)、根本类型。
  • 2)、方针类型。

可是,其实 Groovy 中并没有V H 0根本类型,Groovy 作为动态言语, 在它的国际中,一切事物都是方针,就如 Python、Kotl= y 3in 相同:一切的根本类型都是属于方针类型。为了验证这个 Case,咱们能够新建一个 groovy 文件,创立一个 int 类型的变量并输出它,成果如下图所示:

能够看到,上面的输出成果为 ‘class javo | * ^ 9 L 8a.lang.Integer’,因而能够验R C : +证咱们的想法是正确的。实践上,Groovy 的编译器会将一切的根本类型都包装成方针类型

变量界说

groovy 变量的界说与 Java 中的办法有比较大的D + 3 7 R l差异,对于 groovy 来说,它有 两种界说办法,如下所示:

  • 1)、强类型界说办法:groovy 像 Java 相同,能够进行强类型的界说,比方上面直接界说的 int 类型的 x,这种办法就称为强类型界说办法,即在声明变量的时分界说它的类型。
  • 2)、弱类型界说办法:不需求像强类型界说办法相同需求提早指定类型,而是经过 def 要害字来界说咱们任何的变量,. X , l u ` F _因为编译器会依据值的类型来为它进行主动的赋值。

下面,咱们就运用 def 要害字来界b 5 T J x说一系列的变量,V v I W d e ; f /并输出它们的类型,来看看是否编译器会识别出对应的类型,其成果如下图所示:

能够看6 . s K到,编译器确实会主动主动推断对应的类型。

那么,这两种办法应该别离在什么样的场景中运用, i R 1 ?呢?

假如这个变量便是用= z A 4 x : f /于当时类或文件,而不会用于其它类或应用模块,那么,主张. 2 ; X运用 def 类型,因为在这种场景下o z Y D U弱类型就足够了

可是,假如你这个类或变量要用于@ C – l T R其它模块的,= 2 8 ] ( _ : : u主张不要运用 def,还是应该运用 Java 中的那种强类型界说办法Y c [ ? 6 4 t e,因为运用强类型的界说办法,它不能动态转化为其它类型,它能够确保外界传递进来的值必定是正确的。假如你这个变量要被外界运用,而你却运用了 def 类型来界说它,那外界需求传递给你什么才是正确的呢?这样{ q r o D w / ^会使调用方很疑惑。

假如此刻咱们在后面的代码中改变上图中 x1 的值为 String 类型,那么 x1 又会被编译器推断为 String 类型,如下图所示:

于是咱们能够猜测到,其实运用 def 要害字界说出来的变量便是 Obejct 类型。

Groovy 中的字符% A V

Groovy 中的字符串与 JaS 5 ^ q ; D [ W *va 中的字符串有比较大的不同,所以这儿咱们需求侧重了解一下。

Groovy 中的字符串除了承继了 Java 中传统 String 的运用办法之前,还 新增 了一个 GString 类型,它的运用办法至少有七、八种,可是常用的有三种界说办法。此P o y r K{ t # Z h C在 GString 中新增了一系列的操作符,这能够让咱们对 String 类型的变量有 更快捷的操作。终究,在 GString 中还 新增 了一系列好用的 API,咱们也需求侧重学习一下。

GroovI _ p % V z N 0 Gy 中常用的三种Y T x字符串界说办法

在 Groovyd q Z 6 W T e 中有 三种常用 的字符串界说办法,如下所示:

  • 1)、单引号 ” 界说的字符串
  • 2)、双引号 “” 界说的字符串
  • 3)、三引号 ‘””‘ 界说的字符串

首要,需求阐明的是,’不管是单引号、双引号还是三引号,它们的类型都是 java.lang.String’。

那么,单引号与三引号的差异是什么呢?

既生瑜何生亮,其实否则。当咱们编写的单引号字符串中有转义字符的时分,需求增加 ”,而且,当字符串需求具有多行格局的时分,强行将单引号字符串分成多行格局会变成由 ‘+’ 号组成的字符串拼接格局

那么,双引号界说的变量又与单引号、三引z x 号有什么差异呢?

双引号不同与单、三引号,它界说的是一个可扩展的变量。] o % @ A ) N N这儿咱们先看看两种双引号的运用办法,如下U b p H % ~ D /图所示:

在上图中,第一个界说的 author 字符串便是惯例的 Stn A K S kring 类型d e 6 ( R ` k的字符串,而下面界说的a ; o X 9 u x study 字符串便是可扩展的字符串,因为它里面运9 [ I ; + = n 9用了 ‘${author}’ 的办法引用了 author 变量的内容。而且,从其终究的类型输出能够看到,可扩展的类型便是 ‘org.codehaus.groovy.runtime.GStringImpl’ 类型的。

需求留意的是,可扩展; 2 v的字符串是能够扩展成为恣意的表达式,例如数学运算,如下图所示:

有了 Groovy 的这种可扩展的字符串,咱们就能够 防止 Java 中字符串的拼接操作,提高 Java 程序运行时的功用

那么,已然有 String 和 GString 两种类型的字符串,它们在e x V ( N Q * E彼此赋值的场景下需求不需求先强转再赋值呢?

这儿,咱们能够写一个 小栗子 来看看实践的情L $ ? ! = A况,如下图所示:

能够看到,咱们将 succes/ k : 3 c l C ks 字符串传入了 come 办法,可是终究得到的类型为 result,所以,能够阐明 编译器能够帮咱们主动在 String 和 GString 之间彼此转化,咱们在编写的时分并不需求太过重视它们的差异

2、Groovy 闭包(Closure)

闭包的实质其实便是一个代码块,闭包的中心内容能够归结为如下三点:

  • 1)、闭包概念

    • 界说
    • $ } Y x包的调用
  • 2)、闭包参数

    • 普通参数
    • 隐式参数
  • 3)、闭s ` y F . / t包回来值

    • 总是有回来值

闭包的调用

clouser.call()
clouser()
defxxx={paramters->code}| b ? r W ] G
defxxx={纯code}

从 C/C++ 言语的角度看,闭包和函数指x / # Q针很像,闭包能够经过 .call 办法来调用,也能够直接调用其结构函数,代码如下所示:

闭包方针.call(参数)
闭包方针(参数)

假如闭包没界说参数的话,则隐含有一个参数U { ) D ? ],这个参数名字叫 it,和 this 的效果类 似。it 代表闭包的参数。表明闭包中没有参数的示例: K O @ c代码:

defnoParamClosure={->true}

留意点:省掉圆括号

函数终究一个参数都是一个闭包,类似于回调函数的用法,代码如下所示:

taskJsonChao{
doLast({
println"loveispeace~"- P z X R
}
})

//似O d O P | { ] H q乎好像d: C 2oLast会当即履行相同
taskJsonChao{
doLast{
printlnW ^ q , 4 ~"lP = P ? K M B Noveispeace~"
}
}

闭包的用法

闭包的常见用法有如下 四种:

  • 1)、与根本类型的结f 6 B 8 = r L +合运用。
  • 2)、与 String 类的结合运用。
  • 3)、与数据结构的结合运用。
  • 4)、与文件等结合运用, ; * 2 2 n

闭包进阶

  • 1)、闭包的要害变量

    • this
    • owner
    • delegate
  • 2)、闭包托付策略

闭包的要害变量

this 与 owner、delegate

其差异代s o T f q Z D D码如下代码所示:

defscrpitClouser={
//代表闭包界说处的类
printlQ ) s J ( in"scriptClouser"thisd - q o X $ 8:"+this
//代表闭F I D包界说处的类或许方针
printlin"
scriptClouser"this:"+owner
//代表恣意方针,默许与ownner一向
printlin"scriu ^ W &ptClouser"this:"+delegate
}

// u : p m q t E输出都是scrpitCl* _ ` 4 ) m wouse方针
scrpitClo4 y ruser.call()

defnestClouser={
definnnerClouser={
//代表闭包界说处的类
printlin"
scriptClouser"t0 C _ lhis:"+this
//代表闭包界说处的类或许方针
printlin"scriptClouser"thim } h T r P Fs:"+owner
//代表恣意方针,默许与ownner一向
printlin"
scriptClouser"this:"+delegate
}
in& % Q . ? - g @ Dnn/ 5 S H 9 T F w kerClouser.calb S E 5 % U Y il()
}

//this输出的是nestClousj W p , zer方针,而owner与delegate输出的都是innnerClouser方针^ s b A j
nestClouser.call()

能够看到,假如咱们直接在c g % l j $类、办法、变量中界说一个8 X | L d Y W q闭包,那么这三种要害变量的值都是J . R + = o =相同的,可是,假如咱们在闭包中又嵌套了一个闭包,那么,this 与 owner、delegate 的值就不再相同了。换言之,this 还会指h X 4 H r _ _向咱们闭包界说l , r = e处的类或许实例自身,而 owner、dG $ u . m 5 W 0 uelegate 则会指向离它最近的那个闭包方针

delegate 与 thZ 4 6 i Iis、owner 的差异

其差异代码如下代码所示:

defnestClouser=s ^ T u % } @ N 0{
definnnerClous [ : ! 9 @ Iser={
//代表闭包界说处的类
printlin"scriptClouser"this:"+this
//代表闭包界说处的类或许方针
printlin"
scriptClouser"this:"+owneV e M w v U ] mr
//代表恣意方针,默许与ownner一致
printlin"scriptClouser"this:"+delegate
}

//修正默许的del* $ T z ( r % hegate
innne_ 7 I W 4 : H (rClousC # mer.delegate=p
innnerClouser.call()
}

nestClouser.call()

能够看到,delegate 的值是能够修正的,而且只是当咱们? @ I w U %修正 delegate 的值时,delegate 的值才会与 ownner 的值不相同

闭包的托付策略

其示例代码如下所示:

defstu=newStudent()
deftea=newTeacher()
stu.pretty.deleg5 K ` r & 6ate=tea
//要想使pretty闭包的delegate修正收效,有必要挑选其托付策略为Closure.DELEGATE_ONLY,默许是Closure.OWNER_FIRST。
stu.pretty.resolveStrategy=Closure.DELEGATE_ONLY
printlnstu.toString= ] 4 2 T()

需求留意的是,要想使上述 pretty 闭包的 delegate 修正收效,有必要挑选其托付策略为 Closure.DELEGATE_ONLY,默许是 Clob ? o h jsure.OWNER_FIRST 的。

3、Groovy 数据结构

Groovy 常用的数据结构有如下 四种:

  • 1)、数组
  • 2)、List
  • 3)、Map
  • 4)、Range

数组的运用和 Java 言语类似,最大a a ^ ; J / J 的差异可能便是界说办法的扩展,如下代码所示:

//数组界说
defarray=[1,2,3,4,5]aC n [ ( H Csint[]
int[]array2=[1,2,3,4,5]

下面,咱们看看其它三种数据结构。

1、List

即链表,其底层对应9 Y { Java 中的 List 接口,一般用 ArrayList 作为真实的完结类,List 变量由[]界l L J p @ a说,其元素! f 5 B能够是任何方针K / u q

链表中的元素能够经过索引存取,而且 不必担心索引越界。假如索引超过当4 j 时链表长度,List 会主动往该索引增加元素。下面,咱们看看 Map 最常运用的几个操作。

1)、排序

deftest=[100,"hello",true]
//左移位表明向Lis9 R # ) O ~t中增加新元素
test<z | N g B H g B;<200
//list界说
deflist=[1,2,3,4,5]
//排序
list.sort()
//运用自己的排 G d序规则
sortList.sort{a,b->
a==b?0:
Math.abs(a)<Math.abs(b)?1:-1
}

2)、增加

//增加
list.add(6)
list.4 k ? h u _ ^ BleftShift(7)
list<<8

3)、删除

//删除
lisR F . 8 0 |t.remoY ) g rve(7)
list.removf / T 9 Y , heAt(7)
list.removeElement(6)
list.remoc ~ % i x k 0veAll{rU 5 ? y s D ) ]eturnit%2==0}

4)、查} l L 4 2 y @ K `

//查找
intresult=findLis| Q ] Ft.find{returnit%2==0}
defresult2=findList.findAll{returnit%2!=0}
defresult3=findList.any{returnit%2!=0}
defresult4=findList.es X Y m ( Z Pvery{returnit%2==0}

5)、获取最小值、最大值

//最小值、最大值
list.min()
list.max(returnMath.abs(it))

6)、计算满意条件的数量

//计算满u 8 k意条件的数量
defnum=O g L lfindList.count{returnit>=2}

Map

表明键-值表,其 底层对应 Java 中的 LinkedHashMap

Map 变量由0 Z c 6 m ) R[:]界说,冒号左边是 key,右边是 Value。key 有必要是[ G ? . w / l ~ /字符串,value 能够是任何方针。另外,key 能够用 ” 或 “”b J m i b 包起来,也能够不必引号包起来。下面,咱们看看 Map 最常运用的几个操作。

1)、存c I V ^ @ A ; – v

其示例代码如下所示:

aMap.keyName
aMZ P k @ 2 Cap['keyName']
aMap.anotherkey="iammap"
aMap.anotherkey=[a:1,b:2]

2)、each 办法

假如咱们传递的闭包5 c O o G q M , !是一个参数,那么它就把 entry 作为参数。假如咱们传递的闭包是 2 个参数,那么它就把 key 和 value 作为参数。

defresult=""
[a:1,b:2].each{key,value-&g^ 8 kt;
result+="$key$value"
}

assertresult==v ] R : { V D "a1b2"

defsocre=X q n % B 7 D e""
[a:1,b:2].each{entry->
result+=entry
}

assertresult=="a=1b=2"

3)、eachWithIndex 办法

假如闭包采用两个参数,则将传递 Map.Entry 和项意图索引(从零开端的计数器);否则,假如闭包采用三个参数,则将传递键,值和索引。

defresult="8 Y E x ! C"
[ac $ =:1,b:3].eachWithI. ; t p b i d N @ndex{key,value,# D eindex->result+="$index($key$value)"}
assertresult=="0(a1)1(b3)"

defresult=""
[a:1,b:3].eachWithIndex{entry,inde` G ] B ; Q %x->result+="$index($entry)"}
assertresult=="0(a=1)1(b=h P g k n t 5 Q3)"

4)、groupBy 办法

按照闭包的条件进行分组,代码如下所示:

defgroup=students.groupBy{defstudent->
returnstudent.value.score>=60?'及格':'不及格'
}

5)、findAll 办法

它有两个参数,L ~ G 3 ~ AfindAll 会将 Key 和 Value 别离传进 去。而且,假如 Closure 回来 true,表明该元素是% = A自己想要的,假如回2 ] [ W j T b z ?来 false 则表明该元素不是自己要找的。

RaX O { = 7 }ngt i H ve

表明范围,它其实是 L z @ t ` ) %ist 的一种拓展。其由 begin 值 + 两个点0 g k ? C : : + end 值表明。假如不想包含终究一个元素,则 begin 值 + 两Z R r ^ 8 k个点 + < + end 表明。咱们能够经过 aRange.from 与 aRange.to 来获对应的鸿沟元素

假如需求了解更多的数据结构操作办法,咱们能够直接查 Groovy API 详细文档 即可。

4、Groovy 面O / / D A /向方针

假如不` a F J D声明 public/private 等拜访权限的话,Groovy 中类及其变量默许都是 public 的

1)、元编程(Groovy 运行时)

Groovy 运行时的逻辑处理流程图如下所示:

为了更好的讲解元编程的用法,咱们先创立一个 Person 类并调用它的 cry 办法,代码如` r o & ~ ?下所示:

//第一个groov_ e o U dy文件中
defperson=newPerson(name:'Qndroid',age:26)
printlnperson.cry()

//第二个groovy文件中
classPersonimplementsSerializable{

Stringname

Integerage

defincreaseAge(Integeryears){
this.age+=years
}

/**
*一个办法找不到时,调用它代替
*@paramname
*@paramargs
*@return
*/

definvokeMethod(Stringname,Objectargs){

return"themethodis${name},th# I 6 U [ zeparamsis${args}"
}


defmethodMissing(Stringa G Oname,Objectargs){

return"themethod${name}ismissing"
}
}

为了完结元编程,咱们需求运用 metaClass,详细的运用示例如下所示:

ExpandoMetaClass.enableGlobally()
//为类动态的增加一个特点
Person.metaClass.sex='male'
defperson=newPerson(name:'Qndroid',age:26)
printlnperson.sex
person.sex='female'
println"thenewsexi* & f # ws:"+person.sex
//为类动态的增加办法
Person.metaClass.s` . |exf N s B ` * 9 z TUpperCase={->sex.toUpperCase()}
defperson2=newPerson(name:'Qndroid',age:26)
printlnperson2.sexUpperCase()
//为类动态的增m P ? b 9 c 1 _ Q加静态办法
Person.metaClass.static.createY Y w _ q KPerson={
Stringname,intage->newP+ a 7 q U nerson(name:name,age:age)
}
defperson3? d T a E [ o=Person.createPerson('renzhiqiang',26)
printlnperson3.name+"and"+person3.age

需求留意的是经过类的 metaClass 来增加元素的这种办法每次运用时都需P [ h P b f H L求重新增加,走运的是,咱们能够在注入前调用全局收效的处理,代码如下所示:

Ex* o h + @pandoMetaClass.enableGlobally()
//在应用L ? 4 I x | B J程序初始化的时分咱们能够为第三方类增加办法
Person.metaClass.static.createPe1 H &rson={Stringname,
intage->
newPersr I R D H _on(name:name,age:age)
}

2)、脚本中的变量和效果域

对于每一个 Groovy 脚本来说,它都会生成一个 static void main 函数,main 函数中会调用一个 run 函数,脚本中的一c / B 5 E : : E N切代码则包含在 run 函数之中。咱们能够经过如下的 groovyc 指令e t m Q R M用于将编译得到的 class 文件拷贝到 classes 文件夹下:

//groovyc是groovy的编译指令,-dclasses用于将编译得到的class文件拷贝到class5 @ Mes文件夹下
groovyc-dclassestest.groovy

当咱们在 GroovyX ] c X 3 脚本中界说一个变量时,因为它实践上是在 run 函数中创立的,所以脚本中的其它办法或其他脚本是无法拜访它的。这个时分,咱们需求运用 @Field 将当时变量标记为成员变量,其示例代码如下所示:

importgroovy.transform.Field;

@Fieldauthor=JsonChao

四、文件处理

1、惯例文件处理

1)、读文件

eachLine 办法

咱们能够运用 eachLine 办法读该文件M _ v i中的每一行,它仅有的参数是一个 C| % flosurT Q ) B ie,ClM ] K 3 v / [ osure 的参数是文件每一行的内容。示例代码如下所示:

deffil c [ 4le=newFile(文件r V % #名)2 j E * Y w (
file.eachLine{StringoneLine->
printlnoneLine
}

deftex2 S 5 G a , m Tt=d b O O -file.getText()
deftext2=fil= N I | Ee.rea{ B [ y e t @ ( LdLines(Y e q 2)

file.eachLine{oneLine,lineNo->
println"${lineNo}${oneLine}"
}

然后,咱们能够运用 ‘targetFile.bytes’ 直接得到文件的内容。

运用 InputStream

此外,咱们也能够经过流的办法进行文件操作,如下代码所示:

//操作ism,终究记得关掉
dej v | X ffism=targetFile.newInputStream()
//dosth
ism.clos% 4 7 N A $ ie

运用闭包操作 inputStream

运用闭包来操作 inputStream,其功用愈加强壮,引荐运用这种写法,如下所示:

targetFile.withInputStream{ism-5 W 0  9 w H ^&gC i O V ) rt;
//操作ism,不必close。Groovy会主动替你close
}

2x b h U Z : } $)、写文件

关于写文件有两种v 0 6常用的操作形式,即经过 withOutp1 V # KutStream/withIA ~ c ( a M Vnpuu n : @ I # E QtStream 或 withReader/withWriter 的写法。示例代码如下所示:

经过 withOutput– x 5 m H K X 0 –Stream/、withInputStream copy 文件

defsrcFile=newFile(源文件名)
deftargetFile=newFile(方针文件名)targetFile.withOutputStream{os->
srcFile.withInputStream{ins->
os&s ] , p Z K @lt;<ins//运用OutputStream的<<操作符重载,完结从inputstream到OutputStream//的输出
}
}

经过 withReadeg d 0r、withWriter copy 文件

defcopy(Stringc 4 b ^ | GsourcePath,StringdestationPath){
try{
//首要创立方针文件
defdesFile=newFile(destationPath)
if(!desFile.exists()){
desFile.createNewFile()
}

//开端copy
newFilej } z R L(sourcePath).withReader{reader->
deflines=reader.readLines; Q 7 X @ 1()
desFile.withWriter{writer->
lines.each{line->
writer.append(line+"rn")
}
}
}
returntrue
}catch(Exceptione)@ T 4 g i ! F{
e.printStackTrace()
}
returnfalse
}

此外,{ e ( T X咱们也能够经过 withObjectOutputStr5 x T v a 3 ; Weam/withObjectInputStream 来保存与读取 Object 方针。示例代码如下所示:

保存对应的 Object 方针到文件中

defsaveObject(ObjectD c G d H W Y 1object,Stringpath){
try{
//首要创立方针文件
defdZ z y @ ?esFile=newFile(path)
if(!desFile.* ) ` X z U Y h Qexistsy , & 4 :()){
desFile.createNewFile()
}
desFile.withObjectOe ] YutputStream{out-&gy * lt;
out.writeObject(objeq x [ o Cct)
}
returntrue
}ca5 l : 7 q z S 6 0tch(Exceptione){
}
returnfalse
}

从文件中读取 Obje) Z zct 方针

defreadObject(Stringpath){} 7 9 x i ,
defobj=null
try{
deffile=newFile(path)
if(5 Q * U a 9 Y |file==null||!file.exists())returnnull
//从文件中读取方针
file.withObjectInputStream{input->
obj=input.readObjU X W d b C a / gect()
}
}catch(Exceptione){

}
returnobj
}

2、XML 文件操作

1)、获取 XML 数据

首要,咱们界说一个包含 XML 数据的字符串,如下所示:

finalStringxml='''
<responseversion-api="2.0">
<value>
<boo1 g U T lksid="1"classification=Q U 7"android">
<bookavailable="20"id="1">
<titlt ~ U w F v D I =e>张狂Android讲义</t; # & V [ F l Eitle>
<authorid="1">李刚</author>J P 7 S a ~;
</book>
<boc v ^ / y R I s )okavailable="14"id="2">
<title>第一行代码</title>
<b v ( z ] 1 -authorid="2">郭林</author>
</book>
<bookavailable="13"id="3R | Z ^ C O B 0">
<title>Android开发艺术探究</title>
<authorid="3"&. J C I ~ m c $ /gt;任玉刚</authM v N & y 3 ` 5 *or>
</book>
<booka} S c *vailable="5"id="4">
<title>I n 6 7 K;And| 1 lro7 w Y k + ki G B 0d源码规划模式</title>
<authorid="4">何红辉</author>
</book>
</P ) O Rbooks>
<booksid="2"classification="web">n Q u :
<bookavailable="10"id="1z s K">
<title>Vue从入门到精通&lA 1 N gt;/title>
<authorid="4"&g| 7 1 1 2 ) c =t;李刚</author>
</bo H } f wok>
</books>
</value>
</response>
'''

然后,咱们能够 运用 XmlSluD + [ z ` e ^ c +rper2 v g ^ x 来解析G T –此 xml 数据,代码如下所示:

defxmlSluper=newXmlSlurz & k  @per()
defresponse=xmlSluper.parseText(xml)

//经过指定标签获取特定的特点值
printlnresponse.vaz Z P #lue.books[0].book[0].title.text()
printlnresponse.value.books[0].book[0].authorF l P E U.text()
printlnresponse- L M.value.books[1].book[0].@available 3 U @ 8ea B k % w

deflist=[]
resd j ] A x a & & uponse.value.books.each{books->
//下面开端对书结点进行遍历
books.book.each{bo- | G D v p : Hok->
defauthor=book.author.text()
if(author.equals('李刚')){
list.add(book.title.text())
}
}
}
printlnlD 6 0 ? Q ^ 5isto Q D R f N ? 1.toListStrx c E [ w [ 4ing()

2)、获取v } } y ? 5 v 2 & XML 数据的两种遍历办法

获取 XML 数据有两种遍历办法:深度遍历 XML 数据 与 广度遍历 XML 数据,下面咱们看看它们各自的用法,如下所示:

深度遍历 XML 数据

deftitles=response.depthFirst().findAll{book->
returnbook.author.text()=='李刚'?true:false
}
printlntitles.toListString()

广度遍历 XML 数据

defnal ` Z Y L U _ N 0me=response.value.books.children(){ 8 Q K c k v.b b { p B ~ w 3findAll{node->
node.% + b Fname()=='book'&am1 A v ;p;&node.@id=='2'
}.collect{node->
returnnode.title.text()
}Q f ] k y & l

在实践运用中,咱们能够 运用 XmlSlurper 求获取 AndroidManifest.xml 的版本号(versionName),代码如下所示:

defandroidManifU J aest=newXmlSlurper().parse("AndroidMS { ^ ranifest.xml")printlnandroidManifest['@android:versionI * p g }Name']
或许
priH ~ 1 X W ( CntlnandroidManifest.@'android:vers. R ] }ionName'

3)、F X M y : B c @生成 XML 数据

除了运v Y 0 R $ 7用 XmlSlurper 解析 XML 数据之外,咱们也能够 运用 xmlBuilder 来创立 XML 文件$ % T @ D X g f x,如下代码所示:

/**
*生成xml格局数据
*<langstype='current'count='3'mainstream='true'>
<languageflavor='static'version='1.5'>Java</language>
<languageflavor='dynamic'version='1.6.0'>Groovy</8 B - . G 1language>
<languageflA t = S ^avor='J 4 3 3 : # D Bdynamic'version=_ f - | u c y N5 l i l B 5'1.9'>JavaScript</language>
</langs>
*/

defsw=newStringWriter()
//用来生成xml数据的中心类
defxmlBuilder=newMarkupBuilder(sw)
//根结点langs创立成功
xmlBuilde) b 7 d ~ _r.langs(type:'current',count:'3',
mainstream:'true'){
//第一个language结点
lang& ? Duage(flavorl + + d:'static',version:y S L u $ ; @ SY A _ y S j 1 6'1.5'){
age('16')
}
language(flavor:'dynamic',ve= m % rsion:'1.6- 8 h 0 ='){
age('2 S 4 q 1 f + Y10')
}
language(i # ( | l i T ] 0flavor:'dynamic',version:J o / `'1.9','` r % l LJavaScript')
}

//printlnsw

deflangs=newLangs()
xmlBuilder.langs(type:langf U L X F y Ps.tyT M R N epe,count:langs.count,
mainstream:langs.mainstream){
//遍历一切的子结点
langs.languager C K b ys.each{ ( ] f | h ] G{lang-&/ D = 8gt;
language(flavor:lang.flavor,
version:laE 8 Ang.version,lang.value)
}
}

printlnsw

//对应xml中的langs结点
classLangs{d B s y R U U
Stringtype='current'N # 7 ~ 7 k
intcount=3
booleanmainstream=true
deflanguages=[
n? I SewLanguage(flavor:'static',
vers : ) * p 9 W nion:'1.5',` 1 ( hvalue:0 % B K o ~'Java'),
newLanguage(flavor:'dynamic',
version:'1.3',value:'Groovy'),
newLaN ( @ T 9 P `nguage(flavor:'dynamic',
version:'1.6',value:'JavaScript')
]
}
//对应xml中的languang结点
classLanguage{
Stringflavor
Stringversion
Strin3 O W e ggvalue
}

4)、Groovy 中的 json

咱们能够 运用 Groovy 中供给的 JsonSlurper 类去代替 Gson 解析网络呼应,这样咱们在写插件的时分能够防止引入 GsA z | c = )on 库,其示例代码如下所示:

defreponse=
getNetworkData) e 7 ] B(
'http://yuexibo.top/y0 v G : I w W bxbApp/course_detail.jsonP C : Z .')

printlnreponse.data.head.name

defgetNetworkDat9 l x - ,a(Stringurl)w j [ ) $ S{
//发送http请求
defconnection=newURL(url).openConnection()
connection.setReque~ f I xstMethod('GET')
connection.connect()
defresponse=connection.content.text
//将json转化为实体方针
defjsonSluper=newJsonSlurper()
returnjsonSluper.parseText(response)
}

五、总结

g : T ! S f p这篇文章中,咱们从以下 四个方面 学习了N y ; k i Q ) Groovy 中的必备中心语法:

  • 1)、groovy 中的变量、字符串、循环等根本语法。
  • 2)、groovy 中的数据结构:列表、映射、范围。
  • 3)、groovy 中的办法、类等面向方针、强壮的运行时机制。
  • 4)、groovy 中对普通文件、u P ! h s 6 YXML、json 文件的处理。

在后面咱们自界说 Grt k u s c D j nadle 插件的时分需求运用到这些技* ! Q 8 ) { U t巧,因而,把握好 Groovy 的重要性不言而喻,只有厚实根底才能让咱们走的更远。

参阅链接:

  • 1、Groovy API 详细文档

  • 2、《慕课网之Gradle3.0主动化项目构建技术精讲+实战》1 – 5章

  • 3、《深化了解 Android 之 Gradle》

  • 4、Gradle从入门到实战 – Groovy根底

  • 5、Groovy脚本根底全攻略

Contanct Me

● 微信:

欢迎重视我的微信:bcce5360

● 微信群:

因为微信群已超过 200 人,费事咱们想进微信群的朋友们,加我微信拉你进群。

● QQ群:

2千人QQ群l ! W Y P g? s l uAwesome-Android学习沟通群,QQ群h T +号:9599B n A W h 836182, 欢迎咱们加入~

About mer [ % U

  • Email: chao.qu521@gmail.com

  • Blog: jsonchao.github.i{ } [ p Uo/

  • 6HU网: juejin.im` + 1 K W 2 w/user/5a3ba9…

很感谢您阅览这篇文章,期望您能将它共享给您的朋友或技术群,这对我J O g m 2 x – q t含义重大。

P | # e D w 3 x J望咱们能成} R v p K $ I为朋友,在 Github、6HU网上一同共享常识。