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 中的 LinkedHashMapRange
--> 范围,它其实是 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 即可
- 闭包定义
def android = {
compileSdkVersion 25
buildToolsVersion "25.0.2"
// 这个对应相应的方法
defaultConfig {
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
}
复制代码
- 准备数据 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
}
复制代码
.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
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。