作爲一名Android開發者,每天使用AndroidStudio,對於項目中build.gradle文件肯定不陌生,裏面有各種各樣的配置。對於一些常用的配置我們肯定爛熟於心,不過有時候去看一些大廠的代碼的時候,經常會發現他們的項目中有很多的gradle的代碼,我們往往因爲不瞭解這些而對了解優秀的項目而帶來一些障礙。
百度百科:Gradle是一個基於Apache Ant和Apache Maven概念的項目自動化構建開源工具。它使用一種基於Groovy的特定領域語言(DSL)來聲明項目設置,目前也增加了基於Kotlin語言的kotlin-based DSL,拋棄了基於XML的各種繁瑣配置。
1 DSL語言介紹
domain specific language(領域特定語言 )
DSL語言特點:解決計算機某一特定領域的問題
核心思想:求專不求多
我們常見的UML,XML,HTML等都是DSL語言,他們都是專注於特定的領域來解決特定的問題。比如HTML就是爲了佈局UI。因爲專注,所以有很多優點比如代碼少,效率高。
2 groovy語言
啥是groovy
- groovy是一種基於JVM的敏捷開發語言
- 結合了Python Ruby Smalltalk等語言的強大特性
- groovy可以和Java完美結合,可以使用Java所有的類庫,也能用於擴展現有代碼。
groovy的特性
- 語法上支持動態類型,閉包等新一代語言的特性
- 可以無縫集成左右已經存在的Java的類庫
- 即支持面向對象編程,也支持面向過程編程
groovy的優勢
- 它是一種更加敏捷的編程語言,可以用更少的代碼完成Java同樣的功能
- 對於Java程序員來說,入門容易,功能強大
- 即可以作爲編程語言,也可以作爲腳本語言
3 groovy環境搭建
官網:http://www.groovy-lang.org/
https://groovy.apache.org/download.html
- 安裝JDK
- 到官網下載groovySDK,解壓到合適位置
- 配置環境變量
無論mac/linux還是window都是這三步只不過每個平臺上的步驟不一樣,學啥語言的都跑不過這幾步。
4 Hello Groovy
學習一個語言基本都是從Hello World開始
開發一個Groovy程序可以使用intellij idea這個編譯器,https://www.jetbrains.com/idea/ 直接去官網下載社區版就可以了,社區版支持Groovy程序的開發。
Groovy和Java完全兼容,所以我們完全可以使用Java代碼來編寫Groovy程序
在intellij idea中新建一個Groovy程序(在新建項目面板的右上角,Groovy library選項中添加下載的groovySDK的路徑),在src文件夾下面新建一個Groovy的class類,寫下面Java代碼
class GroovyTest {
public static void main(String[] args){
System.out.print("Hello Groovy");
}
}
可以直接運行,輸出: Hello Groovy
不過我們是來學Groovy語言的,當然用Groovy來寫會更簡單,簡單到啥程度呢,把上面代碼全刪了,寫下面一句。
print("Hello Groovy")
直接運行 輸出:Hello Groovy
看起來很爽啊!
5 Groovy 語法練習
5.1 變量
groovy中的變量包括 基本類型 ,對象類型,不過groovy中所有類型都是對象類型。基本類型也會被自動裝箱成對象類型比如:
int a = 2;
print(a.class)
輸出結果:class java.lang.Integer
變量的定義:
定義一個變量不僅可以使用強類型定義,也可以使用弱類型定義。比如即可以使用int,double這些來修飾一個變量,也可以直接用def來修飾一個變量,使用def修飾,系統會自動推導類型,比如:
def b = 3
println b.class
def c = 3.14
println c.class
def d = "groovy"
println d.class
輸出結果:
class java.lang.Integerclass java.lang.Integer
class java.math.BigDecimal
class java.lang.String
一般情況下,如果我們是自己在寫一個程序,使用def來定義一個變量會很簡單,如果程序還需要提供給外部人員使用,那使用強類型來定義比較好,不然外部人員可能會比較疑惑需要傳什麼類型的變量。
5.2 字符串
Java中是String
Groovy中是GString
5.2.1 Groovy常用的定義方式:
- 使用單引號
def name1 = 'i am name1'
效果和java中一模一樣(不可擴展) - 使用三個引號
def name = '''i am name'''
,使用三個引號的字符串我們可以在字符串內部保留字符串的格式,比如可以隨便換行 - 使用雙引號定義字符串
def name3 = "i am name3"
(可擴展)
def name1 = 'i am name1'
def name2 = '''i am name2
hello name2
hai name2
'''
println(name2)
def name3 = "i am name3"
def name4 = "${name3} 哈哈"
println(name4)
//${}中可以是任意的表達式,比如
def sum = "2加3=${2+3}"
println(sum)
println(name3.class)
println(sum.class)
輸出:
i am name2
hello name2
hai name2
i am name3 哈哈
2加3=5
class java.lang.String
class org.codehaus.groovy.runtime.GStringImpl
從上面的輸出內容還可以看出:一個雙引號定義的字符串的類型是java.lang.String,當經過${}符號運算過之後變成了org.codehaus.groovy.runtime.GStringImpl類型。雖然類型變了,在平時的程序中這兩個類型一般可以互相使用。
5.2.2 GString的操作符
GString的操作符比Java中的String中的方法多,只要來源是:
- java.long.String
- DefaultGroovyMethods
- StringGroovyMethods(繼承DefaultGroovyMethods)包括普通類型的參數和閉包類型的參數
常用的普通類型參數:
def str = "hello groovy"
def str1 = "hello"
def str2 = "123"
println(str.center(15,'a'))//總長度15,在原字符串兩端加a
println(str.padLeft(15,'a'))//總長度15,在原字符串左邊加a
println(str>str1)
println(str[0])
println(str[0..1])
println(str.reverse())
println(str.capitalize())
println(str2.isNumber())
println(str2.toInteger())
輸出:
ahello groovyaa
aaahello groovy
true
h
he
yvoorg olleh
Hello groovy
true
123
5.3 邏輯控制
switch/case 和java中不一樣 比java中更強大
def x = 12
def res
switch (x){
case 'string':
res = 'string'
break
case [3,4,5,6,"haha"]://列表類型
res = 'string'
break
case Integer:
res = 'Integer'
break
case BigDecimal:
res = 'BigDecimal'
break
case 9..30: //表示一個範圍
res = 'string'
break
default: 'default'
}
println(res)
for循環
//循環一個範圍
for (i in 0..9){
println(i)
}
//循環一個list
for (i in [3,4,5,6,7,8,9,0]){
println(i)
}
//循環一個map
for (i in ["lily":18,"divad":19,"xiaoming":20]){
println(i.key)
println(i.value)
}
while循環、if/else和java中一樣
5.4 閉包
閉包是groovy最強大的功能之一
5.4.1 閉包的概念:就是一個代碼塊
閉包的定義和調用:
def closure = {println "hello groovy"}
//兩種調用的方法
closure.call()
closure()
閉包的參數:
def closure2 = { println "hello ${it}"}
closure2("groovy")
def closure1 = {String name,int age -> println "hello ${name},age ${age}"}
closure1("groovy",15)
會有一個默認的參數,可以使用it
這個關鍵字來接收,如果有多個參數可以使用->
來隔開
閉包的返回值
def closure2 = { return "hello ${it}"}
def b = closure2("groovy")
println(b)
如果不定義return,默認返回的是null
5.4.2 閉包的使用
閉包與基本類型結合使用:
常用方法:
def x = 5
//求階乘
def jiecheng(int num){
int res = 1
//upto裏面實現了循環
1.upto(num,{n -> res*=n})
return res
}
//求階乘
def jiecheng1(int num){
int res = 1
num.downto(1){
n -> res *= n
}
return res
}
//累加
def leijia(int num){
int res = 1
num.times {n -> res +=n}
return res
}
println(jiecheng(x))
println(jiecheng1(x))
println(leijia(5))
閉包與字符串結合使用:
常用方法
def str = 'hello groovy 1 2 3'
//each 遍歷
str.each { String tem -> println(tem.capitalize()) }
//找到第一個滿足條件的 並返回
println str.find{ String tem -> tem.isNumber()}
//找到所有滿足條件的 並返回
println str.findAll(){ String tem -> tem.isNumber()}
//只要字符串中存在數字 就返回true
println str.any{ String s -> s.isNumber()}
//字符串每個元素都是數字 就返回true
println(str.every {String s -> s.isNumber()})
//蒐集所有字符 並大寫 放到list中返回
println str.collect {String s -> s.toUpperCase()}
5.4.3 閉包的進階
閉包關鍵變量 this, owner,delegate
def clouser = {
println("this:" + this)
println("owner:" + owner)
println("delegate:" + delegate)
}
clouser.call()
輸出:
this:GroovyTest@741a8937
owner:GroovyTest@741a8937
delegate:GroovyTest@741a8937
從打印來看都是一樣的,那他們有什麼區別呢?
this:代表閉包定義處的類
owner:代表閉包定義處的類或者對象
delegate:代表任意對象 默認是owner
def outClouser = {
def innerClouser = {
println("this:" + this)
println("owner:" + owner)
println("delegate:" + delegate)
}
innerClouser.call()
}
outClouser.call()
輸出:
this:GroovyTest@306e95ec
owner:GroovyTest$_run_closure1@43dac38f
delegate:GroovyTest$_run_closure1@43dac38f
閉包的委託策略:
delegate可以動態的改變,改變之後可以讓一個對象指向另一個對象。
class Teacher{
String name
def nameStr = {"my name is ${name}"}
@Override
String toString() {
return nameStr.call()
}
}
class Student{
String name
}
Teacher teacher = new Teacher(name: "teacher")
Student student = new Student(name: "student")
teacher.nameStr.delegate = student
teacher.nameStr.resolveStrategy = Closure.DELEGATE_FIRST
println teacher.toString()
輸出:
my name is student
5.5 常用的數據結構
5.5.1 列表
列表的定義
//java的方式定義
def list = new ArrayList()
//groovy的方式定義 它默認也是一個ArrayList
def list1 = [1,2,3]
//定義一個數組
def arr = [1,2,3] as int[]
int[] arr1 = [1,2,3]
列表的操作
增刪
def list = [3,2,4,1,5,9,7,8]
list.add(0)
//追加
list.leftShift(6)
//追加
list << 6
//刪除
//list.remove(1)
//list.removeAt(1)
//刪除所有偶數
list.removeAll {return it%2==0}
println(list)
排序操作
//java中的排序----------
def sortList = [3,5,2,1,-3,-6,-4]
//Collections.sort(sortList)
//println(sortList)
//自定義排序規則
//Comparator comparator = {a,b->a==b?0:Math.abs(a)>Math.abs(b)?1:-1}
//Collections.sort(sortList,comparator)
//println(sortList)
//groovy中的排序-----------
sortList.sort()
//自定義排序規則
sortList.sort(){a,b->a==b?0:Math.abs(a)>Math.abs(b)?1:-1}
println(sortList)
//字符串列表
def stringList = ['hello','ab','a','cde','groovy']
stringList.sort(){it -> return it.size()}
println(stringList)
def findList = [5,6,3,2,1,9,8]
//查找第一個奇數
def res = findList.find(){return it%2 == 1}
//查找所有的奇數
def res1 = findList.findAll(){return it%2 == 1}
//判斷是否有奇數
def res2 = findList.any(){return it%2 == 1}
//判斷是不是全是奇數
def res3 = findList.every(){return it%2 == 1}
println(res3)
println(findList.min())
println(findList.max())
println(findList.min{return Math.abs(it)})
//統計查找偶數的個數
def num = findList.count{ return it%2==0 }
println(num)
5.5.1 Map
Map的定義和使用
//java方式
def map = new HashMap()
//groovy方式
def map1 = ['lily':15,'jack':16,'divad':17]
//查找元素
println(map1['lily'])
//添加元素
map1.xiaoming = 18
println(map1.toMapString())
//添加不同類型的元素
map1.sub = ['a':1,'b':2]
println(map1.toMapString())
groovy中的Map默認是Java中LinkedHashMap
Map的常用操作
def map = ['lily':15,'jack':16,'divad':17,'xiao':14]
//遍歷
map.each {def person -> println "key:${person.key} value:${person.value}"}
map.each {key,value -> println "key:${key} value:${value}"}
//有下標的遍歷
map.eachWithIndex {def person ,int index -> println "key:${person.key} value:${person.value} index:${index}"}
map.eachWithIndex{key,value,index -> println "key:${key} value:${value} index:${index}"}
//查找第一個年齡大於15的
def res = map.find{def person -> person.value>15}
//查找所有年齡大於15的
def res1 = map.findAll{def person -> person.value>15}
//統計大於年齡15的個數
def num = map.count {return it.value>15}
//查找並分組
def group = map.groupBy {return it.value>15?'man':'child'}
println(group.toMapString())
//排序
def sort = map.sort{ def v1,def v2 ->
return v1.value == v2.value?0:v1.value>v2.value?1:-1
}
println(sort)
5.6 範圍 Range
Range是一個輕量級的list,在有些簡單的地方使用會比list更加方便
Range的定義和使用
def range = 1..10
println(range[0])
println(range.from)
println(range.to)
println(range.contains(11))
range.each {println(it)}
for (i in range) {
println(i)
}
def res = getGrade(85)
println(res)
def getGrade(int num){
def result
switch (num){
case 0..60:
result = '及格'
break
case 60..80:
result = '良好'
break
case 80..100:
result = '優秀'
break
}
return result
}
5.7 面向對象
類默認都是public的
interface IAction {
void eat()
void drink()
}
class Person implements IAction{
String name
int age
def increaseAge(int num){
this.age += num
}
@Override
void eat() {
}
@Override
void drink() {
}
}
def person = new Person(name: 'lily',age: 15)
person.increaseAge(10)
println("name: ${person.name} age: ${person.age}")
groovy中元編程
當調用一個類的方法的時候:
- 該類中是否有此方法 有直接調用,沒有去MetaClass中查找是否有
- MetaClass中有調用MeatClass中的方法,沒有看是否重寫了methodMissing()方法,寫了調用該方法,沒寫,看是否重寫了invoke()方法
- 寫了自行invoke方法,沒寫拋出異常(MissingMethodExecption)
動態添加屬性或者方法,可以擴展一些引入的第三方包中的類
def person = new Person(name: 'lily',age: 15)
person.increaseAge(10)
person.cry()
//給一個類動態注入一個屬性
Person.metaClass.sex = 'male'
def person1 = new Person(name: 'lily',age: 15)
println(person1.sex)
//給一個類動態添加一個方法
Person.metaClass.sexUpperCase = { -> sex.toUpperCase()}
def person2 = new Person(name: 'lily',age: 15)
println(person2.sexUpperCase())
//給一個類動態添加一個靜態方法
Person.metaClass.static.createPerson = {
name,age -> return new Person(name:name,age: age)
}
def person3 = Person.createPerson('lily',15)
println("name:${person3.name}")
6 Groovy高級語法
6.1 groovy 對 json的操作
groovy中自帶的操作json的方式非常簡單
def list = [new Person(name: 'lily',age: 15),new Person(name: 'jack',age: 16)]
//對象轉成json
String json = JsonOutput.toJson(list)
println(json)
//json轉成對象
def jsonSlurper = new JsonSlurper()
println jsonSlurper.parseText(json)
//parse方法有很多重載的方法,可傳入不同的值
jsonSlurper.parse()
jsonSlurper.parse()方法有很多重載的方法,可傳入不同的值
6.2 groovy處理xml
解析xml的數據
final String xml = '''<apps>
<app>
<id>1</id>
<name>Google Maps</name>
<virsion>1.0</virsion>
</app>
<app>
<id>2</id>
<name>Chrome</name>
<version>2.1</version>
</app>
<app>
<id>3</id>
<name>Google Play</name>
<version>2.3</version>
</app>
</apps>
'''
def xmlSlurper = new XmlSlurper()
//返回的是xml的根節點
def apps = xmlSlurper.parseText(xml)
println(apps.app[0].name.text())
//深度遍歷
def res = apps.depthFirst().findAll { app ->
return app.name.text()
}
def res1 = apps.children().findAll { app -> app.name() == 'app' }.collect {
app -> return app.name.text()
}
println(res)
println(res1)
生成xml的數據
import groovy.xml.MarkupBuilder
def sw = new StringWriter()
def xmlBuilder = new MarkupBuilder()
xmlBuilder.apps(type:'1'){
app(){
id(1)
name('Google Maps')
version(2.0)
}
app(){
id(2)
name('Chrome')
version(1.0)
}
app(){
id(3)
name('Google Play')
version(3.0)
}
}
println(sw)
通過類自動生成xml
import groovy.xml.MarkupBuilder
def sw = new StringWriter()
def xmlBuilder = new MarkupBuilder()
class apps{
String type = '1'
def apps = [new appsub(id: 1,name: 'Google Play',version: '1.0'),
new appsub(id: 1,name: 'java',version: '2.0'),
new appsub(id: 1,name: 'Google map',version: '3.0')]
}
class appsub{
int id
String name
String version
}
def apps = new apps()
xmlBuilder.apps(type:apps.type){
apps.apps.each { sub ->
appsub(){
id(sub.id)
name(sub.name)
version(sub.version)
}
}
}
println(sw)
6.3 groovy處理文件
所有java中的處理文件的方式都可以使用
groovy中也有自己的封裝的簡單的方法
讀取項目跟目錄下的一個文件並遍歷
def file = new File('../MyGroovy.iml')
file.each {line -> println(line)}
讀取內容爲一個text
def text = file.getText()
println(text)
讀取部分內容
def reader = file.withReader { reader ->
char[] buffer = new char[100]
reader.read(buffer)
return buffer
}
println(reader)
文件寫入
def copy(String fromDir,String toDir){
try {
//創建目標文件
def toFile = new File(toDir)
if(!toFile.exists()){
toFile.createNewFile()
}
//開始copy
def fromFile = new File(fromDir);
if(fromFile.exists()){
fromFile.withReader { reader ->
def lines = reader.readLines()
toFile.withWriter { write->
lines.each {
line->
write.append(line + "\r\n")
}
}
}
}
return true
}catch (Exception e){
e.printStackTrace()
}
return false
}
def copres = copy('../MyGroovy.iml','../MyGroovy.iml2')
println(copres)
對象的保存和讀取
//保存對象
def saveObject(Object obj,String path){
try {
//創建目標文件
def toFile = new File(path)
if(!toFile.exists()){
toFile.createNewFile()
}
toFile.withObjectOutputStream {out->
out.writeObject(obj)
}
return true
}catch (Exception e){
e.printStackTrace()
}
return false
}
//讀取對象
def readObj(String path){
def obj = null
try {
def file = new File(path)
if(file==null||!file.exists()) return null
file.withObjectInputStream {
input->
obj = input.readObject()
}
return obj
}catch (Exception e){
e.printStackTrace()
}
return null
}
def person = new Person(name: '大海',age: 15)
def res = saveObject(person,'../person.bin')
println(res)
def personRes = (Person)readObj('../person.bin')
println(personRes.name)