版權(quán)聲明:本文為LooperJing原創(chuàng)文章哩照,轉(zhuǎn)載請注明出處弧烤!
學習Gradle前涧黄,需要有一個Groovy語言的基礎(chǔ)漓帚,以免被Groovy的語法困擾母债,反而忽略了Gradle的知識,可以大致看下上篇文章Gradle系列第(一)篇---Groovy語法初探1胰默。作為一個菜鳥场斑,很想知道 Gradle 的腳本怎么去寫,也看了很多網(wǎng)上的文章牵署,大多都是從腳本的角度來介紹Gradle漏隐,給我的感覺就是,只記住參數(shù)怎么配置奴迅,卻不知道它們都是函數(shù)調(diào)用青责,有相關(guān)API對應(yīng)的。比如我們很常見的一行代碼 apply plugin: 'com.android.application' 是什么意思呢取具?原來一個 build.gradle 對應(yīng)一個 Project , apply 是一個Project 的一個函數(shù) 脖隶,這段代碼其實就是調(diào)用了project對象的apply方法,傳入了一個以plugin為key的map暇检。完整寫出來就是這樣的:project.apply([plugin: 'com.android.application'])产阱。這樣就明白了。所以我們先要學習Gradle的API块仆,然后才能熟練用Gradle構(gòu)建項目构蹬。
1、Project悔据、Task庄敛、Action的關(guān)系
創(chuàng)建Android項目的時候,每一個項目中都有一個build.gradle文件科汗,我們稱build.gradle文件為構(gòu)建腳本,后面你會了解到這個構(gòu)建腳本定義了一個project和一些默認的task藻烤,在解析 Gradle 的編譯過程之前我們需要理解在 Gradle 中非常重要的兩個對象。Project和Task头滔。每個項目的編譯至少有一個 Project,一個 build.gradle就代表一個project,每個project里面包含了多個task,每個task代表了構(gòu)建過程當中的一個原子性操作怖亭,比如編譯,打包坤检,生成javadoc兴猩,發(fā)布等這些操作。task 里面又包含很多action缀蹄,action是一個代碼塊,里面包含了需要被執(zhí)行的代碼。
2缺前、Gradle對象
Gradle基于Groovy蛀醉,Groovy又基于Java。所以衅码,Gradle執(zhí)行的時候和Groovy一樣拯刁,會把腳本轉(zhuǎn)換成Java對象。Gradle主要有三種對象逝段,這三種對象和三種不同的腳本文件對應(yīng)垛玻,在gradle執(zhí)行的時候,會將腳本轉(zhuǎn)換成對應(yīng)的對象:
Gradle對象:當我們執(zhí)行g(shù)radle xxx或者什么的時候奶躯,gradle會從默認的配置腳本中構(gòu)造出一個Gradle對象帚桩。在整個執(zhí)行過程中,只有這么一個對象嘹黔。Gradle對象的數(shù)據(jù)類型就是Gradle账嚎。我們一般很少去定制這個默認的配置腳本。
Project對象:每一個build.gradle會轉(zhuǎn)換成一個Project對象儡蔓。
Settings對象:每一個settings.gradle都會轉(zhuǎn)換成一個Settings對象郭蕉。
既然當我們執(zhí)行g(shù)radle xxx或者什么的時候,gradle會從默認的配置腳本中構(gòu)造出一個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
}
執(zhí)行g(shù)radlew -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获询,也驗證了在整個執(zhí)行過程中涨岁,只有這么一個對象。
3筐付、Gradle的生命周期
如上圖,執(zhí)行g(shù)radlew -q project
輸出
Root project 'GradleTest'
+--- Project ':app'
+--- Project ':library'
\--- Project ':library2'
每一個Library和每一個App都是單獨的Project卵惦。根據(jù)Gradle的要求,每一個Project在其根目錄下都需要有一個build.gradle瓦戚。build.gradle文件就是該Project的編譯腳本沮尿。因為包含了多個項目,所以還要有一個setting.gradle用于多項目的構(gòu)建较解。Gradle的生命周期總共分成三個階段畜疾,初始化階段,配置階段印衔,執(zhí)行任務(wù)階段啡捶。首先是初始化階段,這個時候settings.gradle會執(zhí)行奸焙。初始化的下一個階段是配置階段瞎暑。配置階段的目標是解析每個project中的build.gradle彤敛,其內(nèi)部的任務(wù)也會被添加到一個有向圖里,用于解決執(zhí)行過程中的依賴關(guān)系了赌。在上圖中墨榄,gradle的解析順序是:rootproject 的setting.gradle,然后是rootproject的build.gradle,然后是各個subproject。最后一個階段就是執(zhí)行任務(wù)了勿她,你在gradle xxx中指定什么任務(wù)袄秩,gradle就會將這個xxx任務(wù)鏈上的所有任務(wù)全部按依賴順序執(zhí)行一遍!
gradle整個編譯過程都是可控的逢并,通過實現(xiàn)TaskExecutionListener和BuildListener可以對整個編譯過程進行監(jiān)聽之剧。下面的代碼打印了一下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() )
}
}
輸出結(jié)果:
beforeExecute printGradleInfo
afterExecute name=printGradleInfo state=org.gradle.api.internal.tasks.TaskState
4砍聊、Project
一般app.build文件的第一行是apply plugin: 'com.android.application'背稼,這句話是什么意思,剛剛解釋過辩恼,我們之前說在 Gradle 中構(gòu)建腳本定義了一個項目(project)雇庙。在構(gòu)建的每一個項目中,Gradle 創(chuàng)建了一個Project類型的實例灶伊,并在構(gòu)建腳本中關(guān)聯(lián)此Project對象疆前。并且Project接口是你在 Gradle API 中訪問一切 的入點,當構(gòu)建腳本執(zhí)行時聘萨,它會配置此Project對象竹椒。調(diào)用project的api來獲取和項目有關(guān)的信息。
task queryInfo<<{
println name
println project.name
}
執(zhí)行命令gradlew -q queryInfo
輸出
queryInfo
app
第一個獲取的是任務(wù)名稱米辐,第二個獲取的是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
}
執(zhí)行命令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解析完成后,開始執(zhí)行task之前芍耘,此時所有的腳本已經(jīng)解析完成址遇,task,plugins等所有信息可以獲取斋竞,task的依賴關(guān)系也已經(jīng)生成倔约,如果此時需要做一些事情,可以寫在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.
舉個列子:過濾掉一些我不想執(zhí)行的task.
def disableDebugBuild(){
//project.tasks包含了所有的tasks浸剩,下面的findAll是尋找那些名字中帶debug的Task钾军。
//返回值保存到targetTasks容器中
def targetTasks = project.tasks.findAll{task ->
task.name.contains("Debug")
}
//對滿足條件的task,設(shè)置它為disable绢要。如此這般巧颈,這個Task就不會被執(zhí)行
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可以用,直接運行g(shù)radlew tasks十籍,其會為你展示所有可用的tasks蛆封。當你創(chuàng)建了一個Android工程,那么將包含Android tasks勾栗,build tasks惨篱,build setup tasks,help tasks围俘,install tasks砸讳,verification tasks等。
項目構(gòu)建過程中那么多任務(wù)界牡,有些test相關(guān)的任務(wù)可能根本不需要簿寂,可以直接關(guān)掉,在build.gradle中加入如下腳本:
tasks.whenTaskAdded { task ->
if (task.name.contains('AndroidTest')) {
task.enabled = false
}
}
tasks會獲取當前project中所有的task宿亡,enabled屬性控制任務(wù)開關(guān)常遂,whenTaskAdded后面的閉包會在gradle配置階段完成。
一般我們定義任務(wù)的時候采用的是task + 任務(wù)名的方式挽荠。例如
task hello << {
println "hello"
}
現(xiàn)在再介紹另外兩種方式克胳,和上面的定義是等價的。
task(hello)<<{
println "hello"
}
task('hello')<<{
println "hello"
}
gradle還提供了一個tasks容器來創(chuàng)建任務(wù)圈匆,通過調(diào)用create方法:
tasks.create(name:'hello')<<{
println "hello"
}
如何獲取一個任務(wù)呢漠另?
將任務(wù)看成項目的屬性的方式
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被執(zhí)行的時候跃赚,其內(nèi)部的Action集合會按次序逐個執(zhí)行笆搓,所以借助doFirst(),doLast()等方法來控制Action在隊列中的順序,同時也是執(zhí)行的順序来累。
task testAction {
doFirst {
println("first")
}
doLast {
println("last")
}
}
輸出
first
last
其中對于doLast這個Action還有一個簡便的寫法
task testAction <<{
println("last")
}
<<就代表doLast操作
task與task之間是有關(guān)聯(lián)的砚作,關(guān)聯(lián)可以使用dependsOn和finalizedBy。
task A <<{
println("i am task A")
}
task B <<{
println("i am task B")
}
A.dependsOn B
執(zhí)行g(shù)radlew -q A
輸出:
i am task B
i am task A
如果是 A.finalizedBy B
i am task A
i am task B
相關(guān)鏈接:
Gradle官網(wǎng)https://gradle.org/