Groovy語言介紹


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對象。

例:

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項目結構
    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
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容