注册

Gradle 爬坑指南 -- 概念初解、Grovvy 语法、常见 API(2)

Groovy 语法


再次强调 Groovy 是基于 java 扩展的动态语言,直接写 java 代码是没问题的。上面的 hello world 就是这么跑起来。Groovy 没啥难的,大家把他当做一个新的语言来学下就行,Groovy 本身比较简单也不用我们学习的多深入,能基本使用就可以了,语法糖也没多少,最要的闭包明白就大成了。用的很少的专业一些的 API 大家 baidu 一下就出来了


1. 不用写 ; 号


一看这个就知道也是往高阶语言上靠 <( ̄3 ̄)> 表!,比较新的语言都这样,基本都是大同小异


int name = 10
int age = "AAA"
复制代码

2. 支持动态类型,但是必须用 def 前缀


def name = 10
def age = "AAA"

name = "111"
println(name)
复制代码

3. 没有基本数据类型了,全是包装类型


Groovy 基于 java,所以 java 的基本数据类型都支持,但是 Groovy 中这些基本数据类型使用的都是包装类型:Integer、Boolean 等


int index = 0
println("index == "+index.class)
复制代码


4. 方法变化



  • 使用 def 修饰,方法可以不用指定返回类型、参数类型,直接返回最后一行。
  • 方法调用可以不写 (),最好还是加上()的好,要不真不好阅读
  • 实际上不管有没有返回值,Groovy 中返回的都是 Object 类型

def to(x, y){
x+y
}

def name = 10
def age = 12

name = to name,age

println(name)
复制代码

5. 字符串变化


Groovy 支持单、双、三引号来表示字符串${} 引用变量值,三引号是带输出格式的


def world = 'world'
def str1 = 'hello ${world}'
def str2 = "hello ${world}"
def str3 =
'''hello
&{world}'''
复制代码

6. 不用写 get/set


Groovy ⾃动对成员属性创建 getter / setter,按照下面这个用法调用


class Person{
def name
def age
}

Person person = new Person()
person.name = "AA"
person.setAge(123)
person.@age = 128

println(person.name + " / " + person.age)
复制代码

7. Class 类型,可以省略 .class


8. 没有 ===


Groovy 中 == 就是 equals,没有 === 了。而是用 .is() 代替,比较是不是同一个对象


class Person {
def name
def age
}

Person person1 = new Person()
Person person2 = new Person()
person1.name = "AA"
person2.name = "BB"

println("person1.name == person2.name" + (person1.name == person2.name))
println("person1 is person2" + person1.is(person2))
复制代码

9. 支持 xx次方运算符


2 ** 3 == 8
复制代码

10. 三木运算符


def result = name ?: ""
复制代码

11. 支持非空判断


println order?.customer?.address
复制代码

12. Switch 变化


def num = 5.21

switch (num) {
case [5.21, 4, "list"]:
return "ok"
break
default:
break
}
复制代码

13. 集合类型


Groovy 支持三种集合类型:



  • List --> 链表,对应 Java 中的 List 接口,一般用 ArrayList 作为真正的实现类
  • Map --> 哈希表,对应 Java 中的 LinkedHashMap
  • Range --> 范围,它其实是 List 的一种拓展

// --> list 
def data = [666,123,"AA"]
data[0] = "BB"
data[100] = 33
println("size --> " + data.size()) // 101个元素

----------------------我是分割线------------------------

// --> map
def key = "888"
def data = ["key1": "value", "key2": 111, (key): 888] // 使用 () key 使用动态值

data.key1
data.["key1"]
data.key2 = "new"

def name2 = "name"
def age2 = 578
data.put(name2, age2)

println("size --> " + data.size()) // 4
println("map --> " + data) // [key1:value, key2:new, 888:888, name:578]
println("key--> " + data.get(key)) // key--> 888

----------------------我是分割线------------------------

// --> range
def data = 1..10
data.getFrom()
data.to()

println("size --> " + data.size())
println("range --> " + data) // range --> 1..10
复制代码

14. 闭包



这个是绝对重点,大家到这里认真学呀 (○` 3′○) 学会这个后面就容易理解了,后面都是闭包的应用



闭包(Closure) 是 Groovy 最重要的语法糖了,我们把闭包当做高阶语法中的对象式函数就行了


官方定义:Groovy中的闭包是一个开放,匿名的代码块,可以接受参数,返回值并分配给变量


// 标准写法,method1 就是一个闭包 (>▽<)
def method1 = { name,age ->
name + age
}

// 调用方式
method1.call(123,888)
method1(123,888)

// 默认有一个 it 表示单个参数
def method3 = { "Hello World!$it" }

// 强制不带参数
def method2 = { ->
name + age
}

// 作为方法参数使用
def to(x, y,Closure closure) {
x + y + closure(111)
}
复制代码

后面大家会经常见到闭包的应用,比如这个自定义 task 任务


task speak{
doLast {
println("AAA")
}
}
复制代码

举这个例子是为了说明,实际闭包都是嵌套很多层使用



  • speak 是个方法,接收一个闭包作为参数,整个外层 {...} 都是一个闭包
  • 外层闭包内 doLast 方法又接收一个闭包作为参数,内层 {...} 又是一个闭包



通过这个例子大家搞清楚这个嵌套关系就好学了,实际就是一层套一层,有的插件写的我都看吐了


Closure 这东西方便是方便,但是 Closure 里面传什么类型的参数,有几个参数
这些可没有自动提示,想知道详细就得查文档了,这点简直不能忍,我想说官方就不能做过自动提示出来嘛~
复制代码

15. delegate 闭包委托


这是 Gradle 闭包常见方式:


class Person {
String name
int age
}

def cc = {
name = "hanmeimei"
age = 26
}

Person person = new Person()
cc.call()
复制代码

cc 是闭包,cc.call() 调用闭包,cc.call(persen) 这是给闭包传入参数,我们换个写法:



  • cc.delegate = person 就相当于 cc.call(persen)

这个写法就是:委托 了,没什么难理解的,我这里就是按照最简单的解释来


至于为什么要有委托这种东西,必然是有需求的。我们写的都是 .gradle 脚本,这些脚本实际要编译成 .class 才能运行。也就是说代码实际上动态根据我们配置生成的,传参数也是动态的,委托这一特性就是为了动态生成代码、传参准备的


后面很多 Gradle 中的插件,其 {...} 里面写配置其实走的都是委托这个思路


举个常见的例子,Android {...} 代码块大家熟悉不熟悉,这个就是闭包嵌套,闭包里还有闭包 -->


android {
compileSdkVersion 25
buildToolsVersion "25.0.2"

defaultConfig {
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
}
复制代码

16. 插件中使用 delegate + 闭包思路


其实思路很简单,每一个 {...} 闭包都要有一个对应的数据 Bean 存储数据,在合适的时机 .delegate 即可



  1. 闭包定义

def android = {
compileSdkVersion 25
buildToolsVersion "25.0.2"

// 这个对应相应的方法
defaultConfig {
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
}
复制代码


  1. 准备数据 Bean

class Android {
int mCompileSdkVersion
String mBuildToolsVersion
BefaultConfig mBefaultConfig

Android() {
this.mBefaultConfig = new BefaultConfig()
}

void defaultConfig(Closure closure) {
closure.setDelegate(mProductFlavor)
closure.setResolveStrategy(Closure.DELEGATE_FIRST)
closure.call()
}
}

class BefaultConfig {
int mVersionCode
String mVersionName
int mMinSdkVersion
int mTargetSdkVersion
}
复制代码


  1. .delegate 绑定数据

Android bean = new Android()
android.delegate = bean
android.call()
复制代码

17. 一样需要 import 导入包、文件



Groovy 常用 API


1. 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>
复制代码

1)xml 解析


def xparser = new XmlSlurper()
def targetFile = new File("test.xml")
GPathResult gpathResult = xparser.parse(targetFile)

def book4 = gpathResult.value.books.book[3]
def author = book4.author
author.text()
author.@id
author['@id']
author.@id.toInteger()
复制代码

遍历 XML 数据


def titles = response.depthFirst().findAll { book ->
return book.author.text() == '李刚' ? true : false
}

def name = response.value.books.children().findAll { node ->
node.name() == 'book' && node.@id == '2'
}.collect { node ->
return node.title.text()
}

复制代码

2)获取 AndroidManifest 配置文件参数


Gradle 解析 xml 的意义也就是 AndroidManifest 配置文件了,不难


def androidManifest = new XmlSlurper().parse("./app/src/main/AndroidManifest.xml")
def app = androidManifest.application
println("value -->" + app.@"android:supportsRtl")
复制代码

3)生成 xml


/**
* 生成 xml 格式数据
* <langs type='current' count='3' mainstream='true'>
<language flavor='static' version='1.5'>Java</language>
<language flavor='dynamic' version='1.6.0'>Groovy</language>
<language flavor='dynamic' version='1.9'>JavaScript</language>
</langs>
*/
def sw = new StringWriter()
// 用来生成 xml 数据的核心类
def xmlBuilder = new MarkupBuilder(sw)
// 根结点 langs 创建成功
xmlBuilder.langs(type: 'current', count: '3',
mainstream: 'true') {
//第一个 language 结点
language(flavor: 'static', version: '1.5') {
age('16')
}
language(flavor: 'dynamic', version: '1.6') {
age('10')
}
language(flavor: 'dynamic', version: '1.9', 'JavaScript')
}

// println sw

def langs = new Langs()
xmlBuilder.langs(type: langs.type, count: langs.count,
mainstream: langs.mainstream) {
//遍历所有的子结点
langs.languages.each { lang ->
language(flavor: lang.flavor,
version: lang.version, lang.value)
}
}

println sw

// 对应 xml 中的 langs 结点
class Langs {
String type = 'current'
int count = 3
boolean mainstream = true
def languages = [
new Language(flavor: 'static',
version: '1.5', value: 'Java'),
new Language(flavor: 'dynamic',
version: '1.3', value: 'Groovy'),
new Language(flavor: 'dynamic',
version: '1.6', value: 'JavaScript')
]
}
//对应xml中的languang结点
class Language {
String flavor
String version
String value
}
复制代码

2. 解析 json


def reponse = getNetworkData('http://yuexibo.top/yxbApp/course_detail.json')

def getNetworkData(String url) {
//发送http请求
def connection = new URL(url).openConnection()
connection.setRequestMethod('GET')
connection.connect()
def response = connection.content.text
//将 json 转化为实体对象
def jsonSluper = new JsonSlurper()
return jsonSluper.parseText(response)
}
复制代码

3. IO


Gradle 中操作文件是比不可少的工作了,Grovvy IO API 大家一定要清楚


1) 获取文件地址


o(^@^)o 大家写插件、task 时获取项目地址这个点总是要会的,下面的代码不光可以使用 rootProject,每个脚本中的 Project 对象也可以使用的,path 和 absolutePath 都行


println(rootProject.projectDir.path/absolutePath)
println(rootProject.rootDir.path)
println(rootProject.buildDir.path)

/Users/zbzbgo/worksaplce/flutter_app4/MyApplication22
/Users/zbzbgo/worksaplce/flutter_app4/MyApplication22
/Users/zbzbgo/worksaplce/flutter_app4/MyApplication22/build
复制代码

2) 文件定位


思路就是把指定 path 加入当年项目的根路径中,再构建 File 对象使用


//文件定位
this.getContent("config.gradle", "build.gradle")

// 不同与 new file 的需要传入 绝对路径 的方式
// file 从相对于当前的 project 工程开始查找
def mFiles = files(path1, path2)
println mFiles[0].text + mFiles[1].text
复制代码

或者这样写也是可以的,会在相应的子项目目录下生成文件,这种不用写 this.getContent(XXX)


 def file = project.file(fileName)
复制代码

3)eachLine 一次读一行


File fromFile = new File(rootProject.projectDir.path + "/build.gradle")

fromFile.eachLine { String line ->
println("line -->" + line)
}
复制代码

line 的 API 还有好几个



4)获取输入流、输出流


File fromFile = new File(rootProject.projectDir.path + "/build.gradle")

fromFile.withInputStream { InputStream ins ->
...... 这里系统会自动关闭流,不用我们自己关
}

def ins = fromFile.newInputStream()
ins.close()
复制代码

5)<< 复制 文件


Grovvy 的语法糖写起来的简便


File fromFile = new File(rootProject.projectDir.path + "/build.gradle")
File toFile = new File(rootProject.projectDir.path + "/build2.gradle")

fromFile.withInputStream { InputStream ins ->
toFile.withOutputStream { OutputStream out ->
out << ins
}
}
复制代码

6)<< copy API 复制文件


copy {
from file(rootProject.rootDir.path+"/build.gradle") // 源文件
into rootProject.rootDir.path // 复制目标地址,这里不用带文件名

exclude()
rename { "build.gradle2" } // 复制后重命名,不写的话默认还是目标文件名
}
复制代码

7)reader/writer


File fromFile = new File(rootProject.projectDir.path + "/build.gradle")
File toFile = new File(rootProject.projectDir.path + "/build2.gradle")

fromFile.withReader { reader ->
def lines = reader.lines()
toFile.withWriter { writer ->
lines.each { line ->
writer.writeLine(line)
}
}
}
复制代码

8)Object


File fromFile = new File(rootProject.projectDir.path + "/build.gradle")
File toFile = new File(rootProject.projectDir.path + "/build2.gradle")

fromFile.withObjectInputStream { input ->
toFile.withObjectOutputStream { out ->
out.writeObject( input.readObject() )
}
}
复制代码

9)获取文件字节数组


def file = new File(baseDir, 'test.txt')
byte[] contents = file.bytes
复制代码

10)遍历文件树


def dir = new File("/")
//eachFile()方法返回该目录下的所有文件和子目录,不递归
dir.eachFile { file ->
println file.name
}
dir.eachFileMatch(~/.*\.txt/) {file ->
println file.name
}

-------------------分割线-------------------

def dir = new File("/")
//dir.eachFileRecurse()方法会递归显示该目录下所有的文件和目录
dir.eachFileRecurse { file ->
println file.name
}
dir.eachFileRecurse(FileType.FILES) { file ->
println file.name
}

-------------------分割线-------------------

dir.traverse { file ->
//如果当前文件是一个目录且名字是bin,则停止遍历
if (file.directory && file.name=='bin') {
FileVisitResult.TERMINATE
//否则打印文件名字并继续
} else {
println file.name
FileVisitResult.CONTINUE
}
}
复制代码

11)序列化


boolean b = true
String message = 'Hello from Groovy'
def file = new File(baseDir, 'test.txt')
// 序列化数据到文件
file.withDataOutputStream { out ->
out.writeBoolean(b)
out.writeUTF(message)
}
// ...
// 从文件读取数据并反序列化
file.withDataInputStream { input ->
assert input.readBoolean() == b
assert input.readUTF() == message
}
复制代码

12)程序中执行shell命令


def process = "ls -l".execute()
println(process)

作者:前行的乌龟
链接:https://juejin.cn/post/6888977881679495175
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1 个评论

写的很好,很全面

要回复文章请先登录注册