Groovy概述
Gradle 采用了 Groovy 語言作為主要的腳本語言
一個 build.gradle 文件,其實是一個 Groovy 類
Groovy 是一個基于 JVM 的語言,代碼最終編譯成字節碼在 JVM上運行
它具有類似于 Java 的語法風格,但是語法又比 Java 要靈活和方便,同時具有動態語言(如 ruby 和 Python)的一些特性。
語言基礎
變量
有兩類可以在生成腳本中聲明的變量: 局部變量和額外屬性
- a、局部變量
在groovy 中,沒有固定的類型,局部變量是用def關鍵字聲明的。
它們只在定義它們的范圍內可以被訪問
def name = 'Jession'
變量定義時,也可以直接指定類型
def int x = 1
單引號與雙引號的區別
單引號中字符串僅僅是字符串,即Java中String
def name1 = 'Jession'
雙引號可以進行轉義,$會被轉義成Jession,因此name2的值為Tony,Jessio
def name2="Tony ,$name1"
- b、額外屬性
定義:用戶可以為增強對象設置額外屬性,可以通過對象的ext屬性進行添加,讀取與設置
//為myTask創建了一個名字為myProperty屬性,值是value
task myTask{
ext.myProperty="value"
}
方法
使用def關鍵字定義方法
指定了函數返回值類型,可以不使用def
參數類型可以不指定
方法如果不指定返回值,默認返回最后一行代碼的值
方法可以不用分號結尾
函數調用也可以不用括號,不推薦(Groovy經常將屬性與函數調用混淆)
//無類型函數
def getNum(def num) {
num * num
}
//指定函數返回類型int,可不適用def
int getNum(def num){
num * num
}
//參數類型不指定
def getNum(num) {
num * num
21
}
類
默認所有的類和方法都是pulic的,可以不用寫
所有類的字段都是private的
通過new關鍵字得到類的實例
使用def接受對象的引用
def people = new People()
類中聲明的字段都默認會生成對應的setter,getter方法
people.getName() 等同于 people.name
數據類型
分類:
Java的基本數據類型
Groovy的容器類
閉包
a.基本數據類型
Groovy中萬物皆對象
基本數據類型對應的事包裝數據類型
即:int為Integer,boolean為Boolean
b.Groovy的容器類
1).List:鏈表
對應Java中的List接口,一般用ArrayList作為真正的實現類
//定義一個List
List list = [1, 2, 3, 4, 5]
//下標2的值為100
list[2]=100
//獲取下標為2的值
assert list[2] == 7
//內容可以不同類型,并且可以重復,可以為null
def list3 = ['a', 1, 'a', 'a', 2.5, 2.5f, 2.5d, 'hello', 7g, null]
2).Map:鍵-值表,其底層對應Java中的LinkedHashMap。
//定義一個Map
def mymap = ['key1':'value1','key2':true]
//獲取map的值
mymap['key1']
3).Range:范圍,它其實是List的一種拓展。
//定義,值為1 2 3 4 5
def range = 1..5
//定義,值為5 6 7
Rangerange = 5..<8
c、閉包(Closure)
閉包是可以用作函數參數和方法參數的代碼塊,
代碼在某處被定義然后在其后的調用處執行
簡單點說就是代表了一段可執行的代碼
定義:
def xxx = {paramters -> code}
def xxx = {無參數,純code} //這種情況不需要->
例:
//含參數
def myClosure={String str->
println(str)
}
調用方式:myClosure("Hello")
//無參數
def myClosure={
println("Hello")
}
調用方式:myClosure()
//多參數
def myClosure={String str,int num->
println("$str ,$num ")
}
調用方式:myClosure("Hello",20)
//參數類型可不寫
def myClosure={ str, num->
println("$str ,$num ")
}
調用方式:myClosure("Hello",20)
如果閉包沒定義參數的話,則隱含有一個參數,這個參數名字叫it,和this的作用類似。it代表閉包的參數:
def list = [1,2,3,4,5] //定義一個List
list.each{ //調用它的each函數
println it
}
Gradle
Gradle官網 https://gradle.org/
Project、Task、Action的關系
創建Android項目的時候,每一個項目中都有一個build.gradle文件,我們稱build.gradle文件為構建腳本
Project:
每個項目的編譯至少有一個 Project,一個 build.gradle就代表一個project
project由多個task組成
- Task:
由多個action組成,action就是一個代碼塊,里面是需要執行的代碼
比如編譯,打包,生成javadoc,發布等
Gradle對象
Gradle基于Groovy,Groovy又基于Java。所以,Gradle執行的時候和Groovy一樣,會把腳本轉換成Java對象
- Gradle三種對象
- a.Gradle對象:
當我們執行gradle xxx或者什么的時候,gradle會從默認的配置腳本中構造出一個Gradle對象在整個執行過程中,只有這么一個對象,Gradle對象的數據類型就是Gradle - b.Project對象:
每一個build.gradle會轉換成一個Project對象。- c.Settings對象:
每一個settings.gradle都會轉換成一個Settings對象。
- c.Settings對象:
例:
1.在build.gradle文件中定義一個Task,如下:
task printGradleInfo{
println "----------------------------------------------- "
println "In posdevice, gradle id is " +gradle.hashCode()
println "Home Dir:" + gradle.gradleHomeDir
println "User Home Dir:" + gradle.gradleUserHomeDir
println "Parent: " + gradle.parent
}
2.執行gradlew -q printGradleInfo
輸出
F:\StudyProject\GradleTest2>gradlew -q printGradleInfo
In posdevice, gradle id is 635573988
Home Dir:C:\Users\wangjing\.gradle\wrapper\dists\gradle-2.14.1-all\8bnwg5hd3w55i
User Home Dir:C:\Users\wangjing\.gradle
Parent: null
如果你在打印gradle的hashCode,得到的輸出也是635573988,也驗證了在整個執行過程中,只有這么一個對象。
Gradle的生命周期
-
項目結構
gradle項目結構
每一個Library和每一個App都是單獨的Project。
每一個Project在其根目錄下都需要有一個build.gradle。
build.gradle文件就是該Project的編譯腳本。
因為包含了多個項目,所以還要有一個setting.gradle用于多項目的構建。
Gradle的生命周期總共分成三個階段,初始化階段,配置階段,執行任務階段
初始化階段
這個時候settings.gradle會執行
配置階段
解析每個project中的build.gradle,其內部的任務也會被添加到一個有向圖里,用于解決執行過程中的依賴關系。
在上圖中,gradle的解析順序是:rootproject 的setting.gradle,然后是rootproject的build.gradle,然后是各個subproject。
執行任務階段
你在gradle xxx中指定什么任務,gradle就會將這個xxx任務鏈上的所有任務全部按依賴順序執行一遍!
gradle整個編譯過程都是可控的,通過實現TaskExecutionListener和BuildListener可以對整個編譯過程進行監聽。下面的代碼打印了一下task的名字。
gradle.addListener(new LifecycleListener())
class LifecycleListener implements TaskExecutionListener,BuildListener{
@Override
void buildStarted(Gradle gradle) {
}
@Override
void settingsEvaluated(Settings settings) {
}
@Override
void projectsLoaded(Gradle gradle) {
}
@Override
void projectsEvaluated(Gradle gradle) {
}
@Override
void buildFinished(BuildResult result) {
}
@Override
void beforeExecute(Task task) {
println("beforeExecute "+task.name)
}
@Override
void afterExecute(Task task, TaskState state) {
println("afterExecute name="+task.name+" state="+state.toString() )
}
}
===========================
輸出結果:
beforeExecute printGradleInfo
afterExecute name=printGradleInfo state=org.gradle.api.internal.tasks.TaskState
Project
Project官方文檔 https://docs.gradle.org/current/dsl/org.gradle.api.Project.html
- 一般app.build文件的第一行是apply plugin: 'com.android.application'
一個 build.gradle 對應一個 Project , apply 是一個Project 的一個函數
這段代碼其實就是調用了project對象的apply方法,傳入了一個以plugin為key的map
完整寫出來就是這樣的:
project.apply([plugin: 'com.android.application'])。
我們之前說在 Gradle 中構建腳本定義了一個項目(project)。在構建的每一個項目中,Gradle 創建了一個Project類型的實例,并在構建腳本中關聯此Project對象。并且Project接口是你在 Gradle API 中訪問一切 的入點,當構建腳本執行時,它會配置此Project對象。調用project的api來獲取和項目有關的信息。
task queryInfo<<{
println name
println project.name
}
執行命令
gradlew -q queryInfo
輸出
queryInfo
app
第一個獲取的是任務名稱,第二個獲取的是Project名稱,如果把queryInfo中的 println name放在外面,他會打印項目名稱。
println name
task check<<{
println project.name
}
結果
app
app
查詢項目的項目信息:
task queryProjectInfo<<{
//項目名
println project.name
//項目相對路徑
println project.path
//項目描述
println project.description
//項目的絕對路徑
println project.projectDir
//項目的build文件絕對路徑
println project.buildDir
//項目所在的group
println project.group
//項目的版本號
println project.version
//項目的ant對象
println project.ant
}
執行命令
gradlew -q queryInfo
輸出
其他應用方法
比如,在解析setting.gradle之后,開始解析build.gradle之前,這里如果要干些事情可以寫在beforeEvaluate。
在所有build.gradle解析完成后,開始執行task之前,此時所有的腳本已經解析完成,task,plugins等所有信息可以獲取,task的依賴關系也已經生成,如果此時需要做一些事情,可以寫在afterEvaluate。
舉列:過濾掉一些我不想執行的task.
def disableDebugBuild(){
//project.tasks包含了所有的tasks,下面的findAll是尋找那些名字中帶debug的Task。
//返回值保存到targetTasks容器中
def targetTasks = project.tasks.findAll{task ->
task.name.contains("Debug")
}
//對滿足條件的task,設置它為disable。如此這般,這個Task就不會被執行
targetTasks.each{
println"disable debug task :${it.name}"
it.setEnabled false
}
}
project.afterEvaluate{
disableDebugBuild()
}
又比如
apply plugin: 'com.android.application'
的原形是
project.apply([plugin: 'com.android.application'])
Task
- 如果你想知道你多少tasks可以用,直接運行gradlew tasks,其會為你展示所有可用的tasks。
- 當你創建了一個Android工程,那么將包含Android tasks,build tasks,build setup tasks,help tasks,install tasks,verification tasks等。
項目構建過程中那么多任務,有些test相關的任務可能根本不需要,可以直接關掉,在build.gradle中加入如下腳本:
tasks.whenTaskAdded { task ->
if (task.name.contains('AndroidTest')) {
task.enabled = false
}
}
tasks會獲取當前project中所有的task,enabled屬性控制任務開關,whenTaskAdded后面的閉包會在gradle配置階段完成。
一般我們定義任務的時候采用的是task + 任務名的方式。例如
task hello << {
println "hello"
}
或者
task(hello)<<{
println "hello"
}
task('hello')<<{
println "hello"
}
gradle還提供了一個tasks容器來創建任務,通過調用create方法
tasks.create(name:'hello')<<{
println "hello"
}
如何獲取一個任務呢?
將任務看成項目的屬性的方式
println tasks.hello.name
println tasks['hello'].name
使用tasks容器來定位
println hello.name
println project.hello.name
tasks.getByPath()方式來獲得
println tasks.getByPath('hello').path
println tasks.getByPath(':hello').path
每個Task包含了Action對象的集合。當Task被執行的時候,其內部的Action集合會按次序逐個執行,所以借助doFirst(),doLast()等方法來控制Action在隊列中的順序,同時也是執行的順序。
task testAction {
doFirst {
println("first")
}
doLast {
println("last")
}
}
輸出
first
last
其中對于doLast這個Action還有一個簡便的寫法
task testAction <<{
println("last")
}
<<就代表doLast操作
- task與task之間是有關聯的,關聯可以使用dependsOn和finalizedBy。
task A <<{
println("i am task A")
task B <<{
println("i am task B")
}
A.dependsOn B
執行gradlew -q A
輸出:
i am task B
i am task A
如果是 A.finalizedBy B
i am task A
i am task B