學習Gradle前,需要有一個Groovy語言的基礎,以免被Groovy的語法困擾,反而忽略了Gradle的知識,可以大致看下上篇文章Gradle系列第(一)篇—Groovy語法初探1。作爲一個菜鳥,很想知道 Gradle 的腳本怎麼去寫,也看了很多網上的文章,大多都是從腳本的角度來介紹Gradle,給我的感覺就是,只記住參數怎麼配置,卻不知道它們都是函數調用,有相關API對應的。比如我們很常見的一行代碼 apply plugin: ‘com.android.application’ 是什麼意思呢?原來一個 build.gradle 對應一個 Project , apply 是一個Project 的一個函數 ,這段代碼其實就是調用了project對象的apply方法,傳入了一個以plugin爲key的map。完整寫出來就是這樣的:project.apply([plugin: ‘com.android.application’])。這樣就明白了。所以我們先要學習Gradle的API,然後才能熟練用Gradle構建項目。
1、Project、Task、Action的關係
創建Android項目的時候,每一個項目中都有一個build.gradle文件,我們稱build.gradle文件爲構建腳本,後面你會了解到這個構建腳本定義了一個project和一些默認的task,在解析 Gradle 的編譯過程之前我們需要理解在 Gradle 中非常重要的兩個對象。Project和Task。每個項目的編譯至少有一個 Project,一個 build.gradle就代表一個project,每個project裏面包含了多個task,每個task代表了構建過程當中的一個原子性操作,比如編譯,打包,生成javadoc,發佈等這些操作。task 裏面又包含很多action,action是一個代碼塊,裏面包含了需要被執行的代碼。
2、Gradle對象
Gradle基於Groovy,Groovy又基於Java。所以,Gradle執行的時候和Groovy一樣,會把腳本轉換成Java對象。Gradle主要有三種對象,這三種對象和三種不同的腳本文件對應,在gradle執行的時候,會將腳本轉換成對應的對象:
Gradle對象:當我們執行gradle xxx或者什麼的時候,gradle會從默認的配置腳本中構造出一個Gradle對象。在整個執行過程中,只有這麼一個對象。Gradle對象的數據類型就是Gradle。我們一般很少去定製這個默認的配置腳本。
Project對象:每一個build.gradle會轉換成一個Project對象。
Settings對象:每一個settings.gradle都會轉換成一個Settings對象。
既然當我們執行gradle xxx或者什麼的時候,gradle會從默認的配置腳本中構造出一個Gradle對象,那麼看一看Gradle對象的信息有哪些。在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
}
執行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,也驗證了在整個執行過程中,只有這麼一個對象。
3、Gradle的生命週期
如上圖,執行gradlew -q project
輸出
Root project 'GradleTest'
+--- Project ':app'
+--- Project ':library'
\--- Project ':library2'
每一個Library和每一個App都是單獨的Project。根據Gradle的要求,每一個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
4、Project
一般app.build文件的第一行是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
輸出
app
:app
null
E:\Programs\android_studio\GradleTest\app
E:\Programs\android_studio\GradleTest\app\build
GradleTest
unspecified
org.gradle.api.internal.project.DefaultAntBuilder@3f1ae6ad
還有若干方法的使用
比如,在解析setting.gradle之後,開始解析build.gradle之前,這裏如果要幹些事情可以寫在beforeEvaluate。
在所有build.gradle解析完成後,開始執行task之前,此時所有的腳本已經解析完成,task,plugins等所有信息可以獲取,task的依賴關係也已經生成,如果此時需要做一些事情,可以寫在afterEvaluate。文檔對afterEvaluate(closure)的解釋是:
Adds a closure to be called immediately after this project has been evaluated. The project is passed to the closure as a parameter. Such a listener gets notified when the build file belonging to this project has been executed. A parent project may for example add such a listener to its child project. Such a listener can further configure those child projects based on the state of the child projects after their build files have been run.
舉個列子:過濾掉一些我不想執行的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'])
dependencies {
compile 'com.google.code.gson:gson:2.3'
}
原形:
project.dependencies({
add('compile', 'com.google.code.gson:gson:2.3', {
// Configuration statements
})
})
看看下面的圖,Project的方法和屬性很多
這是Project官方文檔:https://docs.gradle.org/current/dsl/org.gradle.api.Project.html
5、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
相關鏈接:
Gradle官網https://gradle.org/