Gradle的一些命令記錄:
- 執(zhí)行一個(gè)指定的任務(wù):
gradle -q taskName
// 如:gradle -q hello - 查看所有任務(wù)
gradle -q tasks
- 執(zhí)行多個(gè)任務(wù)
gradle hello hello2
- 排除任務(wù)的執(zhí)行 -x:
gradle hello -x hello2
// 排除hello2執(zhí)行
org.gradle.api.Project 項(xiàng)目
在Gradle中溯乒,一個(gè)項(xiàng)目(project)表示一個(gè)正在構(gòu)建的組件(如:一個(gè)jar文件夹厌,android中,一個(gè)aar文件)裆悄;
Gradle的build.gradle
文件相當(dāng)于Maven的pom.xml矛纹,每個(gè)build.gradle文件在構(gòu)建啟動(dòng)后,對(duì)應(yīng)于org.gradle.api.Project
的一個(gè)實(shí)例光稼,并且能夠通過(guò)project
變量使其隱式可用或南;
一個(gè)Project可用創(chuàng)建新的task
,添加依賴與配置钟哥,實(shí)際上這些都是 project
接口的方法實(shí)現(xiàn)的迎献,如下dependencies
就是調(diào)用 Project的 dependencies 方法,并傳遞閉包腻贰;
dependencies {
provided 'com.android.support:support-annotations:25.3.1'
provided 'com.android.support:support-v4:25.3.1'
}
構(gòu)建Project實(shí)例中,可通過(guò)代碼訪問(wèn)Gradle的所有特性扒秸,如:task創(chuàng)建播演、依賴管理等;當(dāng)訪問(wèn)屬性與方法時(shí)伴奥,不需要使用project
變量写烤;
setDescription("myProject") // 沒(méi)有顯示指定 project變量
println "Description of project $name: " + project.description; // 訪問(wèn)name與description屬性;
在Android開(kāi)發(fā)中拾徙,每個(gè)module都表示一個(gè)Project洲炊,有自己獨(dú)立的 build.gradle腳本;
org.gradle.api.Task 任務(wù)
任務(wù)task對(duì)應(yīng)的是 org.gradle.api.Task
接口尼啡;任務(wù)的一些重要功能有:任務(wù)動(dòng)作(task action)和任務(wù)依賴(task dependency)暂衡;
任務(wù)動(dòng)作:定義了一個(gè)當(dāng)任務(wù)執(zhí)行時(shí)最小的工作單元;
任務(wù)依賴:有些時(shí)候崖瞭,某個(gè)任務(wù)的運(yùn)行之前需要其他Task先運(yùn)行狂巢;
屬性
每個(gè)Project
和Task
實(shí)例都提供可通過(guò) getter與setter方法訪問(wèn)的屬性,如:項(xiàng)目名书聚,任務(wù)名等唧领;
通常藻雌,我們需要自定義一些自己的屬性;比如:Android的編譯版本斩个,minSdk等等胯杭;這就用到擴(kuò)展屬性;
擴(kuò)展屬性
在內(nèi)部受啥,這些屬性以鍵值對(duì)的形式存儲(chǔ)歉摧,添加屬性,使用 ext
命名空間腔呜,在定義擴(kuò)展屬性的時(shí)候叁温,需要使用到 ext
命名空間前綴,如下 :
// 此擴(kuò)展屬性信息核畴,定義在一個(gè)單獨(dú)的gradle文件中膝但,使用的使用
// 在項(xiàng)目的根build.gralde文件中 apply from: "文件名"來(lái)引用;
// gradle配置信息
ext {
COMPILE_SDK_VERSION = 25 // 25 23
BUILD_TOOLS_VERSION = "25.0.2" // 25.0.2 23.0.2
MIN_SDK_VERSION = 14
TARGET_SDK_VERSION = 23 // 這個(gè)千萬(wàn)不能改
// support依賴支持包
compile_support = [
design : "com.android.support:design:25.3.1",
annotations : "com.android.support:support-annotations:25.3.1",
appcompat : "com.android.support:appcompat-v7:25.3.1",
recyclerview: "com.android.support:recyclerview-v7:25.3.1",
]
// 常用庫(kù)包
compile_common = [
gson: "com.google.code.gson:gson:2.7"
]
}
使用的時(shí)候谤草,可省略ext
前綴跟束,如下:
android {
compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion BUILD_TOOLS_VERSION
....
Gradle屬性
Gradle屬性可通過(guò)gradle.properties
文件中聲明直接添加到項(xiàng)目中,此文件位于 <USER_HOME>/.gradle.properties
目錄 或 項(xiàng)目的根目錄下丑孩,這些屬性可以通過(guò)項(xiàng)目實(shí)例訪問(wèn)冀宴;
如果一個(gè)project有多個(gè)module,也只能有一份 gradle.properties文件温学;
比如,在properties文件聲明了(最好在本項(xiàng)目的文件做實(shí)驗(yàn)略贮,不動(dòng)全局的,見(jiàn)下解釋):
myname=zhaoyubetter
通過(guò)直接引用名字來(lái)訪問(wèn)
println(myname) // 來(lái)進(jìn)行訪問(wèn)仗岖;
注意:在android studio 中逃延,android視圖中,顯示的gradle.properties文件是全局的轧拄,與操作系統(tǒng)的是同一個(gè)文件揽祥,如下:
項(xiàng)目中的 gradle.properties
文件需要切換到 project files 視圖,才能看到檩电,如下:
使用Task
默認(rèn)情況下拄丰,每個(gè)新創(chuàng)建的Task都是org.gradle.api.DefaultTask
類型的;
聲明task動(dòng)作
動(dòng)作(action)就是在task中合適的地方放置構(gòu)建邏輯俐末,Task接口提供2個(gè)相關(guān)的方法來(lái)聲明task動(dòng)作:doFirst(Closure)
(可使用 >>)與 doLast(Closure)
(可使用<<左移)料按;
def curVersion = '1.0.0-SNAPSHOT'
task printVersion {
doLast {
println ">>>>>>>>>>>>>>>>>>verson:${curVersion}"
}
}
// or 寫(xiě)成
task printVersion << {
println ">>>>>>>>>>>>>>>>>>verson:${curVersion}"
}
通過(guò) gradle printVersion 可執(zhí)行此任務(wù);
訪問(wèn)DefaultTask屬性
- description屬性:描述任務(wù)的作用鹅搪;
- group屬性: 用于定義task的邏輯分組站绪;
創(chuàng)建Task時(shí),可傳遞丽柿,作為參數(shù):
def version = '1.0.0-SNAPSHOT'
task printVersion(group: 'versioning', description: 'Print project version') {
doFirst {
println ">>>>> before reading the version"
}
doLast {
println ">>>>> version: $version"
}
}
// 或者
task printVersion() {
group = 'versioning'
description = 'Print project version'
定義Task依賴
dependsOn
方法恢准,允許聲明依賴一個(gè)或多個(gè)task魂挂,通過(guò)task依賴關(guān)系圖來(lái)建模完整的task生命周期;
def group = 'myGroup'
task first << { println ">>>> first" }
task second << { println ">>>> second" }
// 依賴多個(gè)任務(wù)
task printVersion(group: group, dependsOn: [second, first]) << {
println ">>>> the version : $version"
}
task third << { println ">>> third" }
third.dependsOn('printVersion') // 聲明依賴時(shí)馁筐,按名稱引用task
輸入:gradle -q third
, 輸出如下:
task 依賴的執(zhí)行順序
Gradle不能保證依賴的執(zhí)行順序,dependsOn只是定義了所依賴的task需要先執(zhí)行敏沉;在gradle中果正,執(zhí)行順序是由task的輸入/輸出規(guī)范自動(dòng)確定的;
終結(jié)器Task
使用 task 的finalizedBy
來(lái)使用一個(gè)特定的終結(jié)器:
task first << { println ">>>> first" }
task second << { println ">>>> second" }
first.finalizedBy second // 執(zhí)行 gradle -q first 時(shí)盟迟,會(huì)觸發(fā) task second
添加任意代碼秋泳,設(shè)置版本信息
gradle就是groovy,所以可以加入一些groovy代碼攒菠,如下:
// 版本
class ProjectVersion {
int major
int minor
int min
boolean release
ProjectVersion(int major, int minor, int min) {
this.major = major
this.minor = minor
this.min = min
this.release = false
}
ProjectVersion(int major, int minor, int min, boolean release) {
this.major = major
this.minor = minor
this.min = min
this.release = release
}
@Override
public String toString() {
return "$major.$minor.$min${release ? '' : '-SNAPSHOT'}"
}
}
def version = new ProjectVersion(1, 0, 0)
task printVersion << {
println(" >>>>>> the verson is ${version}")
}
添加task配置塊
task配置塊會(huì)在task動(dòng)作之前被執(zhí)行迫皱;
- 新建配置文件
version.properties
如下:
major=1
minor=0
min=0
release=false
- 添加
Gradle配置
loadVersion(沒(méi)有<<
或>>
運(yùn)算符) ,并讀取 properties文件
ext.versionFile = file('version.properties')
// loadVersion任務(wù)辖众,沒(méi)有 << or >> 運(yùn)算符卓起,Gradle稱之為 task配置
task loadVersion {
project.version = readVersion()
}
ProjectVersion readVersion() {
println(">>>>> reading the version file")
if(!versionFile.exists()) {
throw new RuntimeException(">>> Required version file does not exist: ${versionFile.canonicalPath}")
}
Properties prop = new Properties()
versionFile.withInputStream { stream ->
prop.load(stream)
}
new ProjectVersion(prop.major.toInteger(), prop.minor.toInteger(), prop.min.toInteger(), prop.release.toBoolean())
}
task printVersion << {
println(" >>>>>> the verson is ${version}") // 訪問(wèn)project的屬性version
}
Gradle構(gòu)建生命周期階段
執(zhí)行g(shù)radle構(gòu)建,會(huì)運(yùn)行三個(gè)不同的生命周期階段:初始化凹炸、配置和執(zhí)行戏阅;
如下圖:
- 初始化階段:gradle為項(xiàng)目創(chuàng)建一個(gè)Project實(shí)例;
- 配置階段:Gradle構(gòu)造一個(gè)模型來(lái)表示任務(wù)啤它,此階段用來(lái)為項(xiàng)目或指定的task設(shè)置所需的配置奕筐;項(xiàng)目的每一次構(gòu)建的任何配置代碼都可以被執(zhí)行,即使只執(zhí)行 gradle tasks;
- 執(zhí)行階段:執(zhí)行task動(dòng)作蚕键;執(zhí)行的順序由他們的依賴決定救欧;如果任務(wù)被認(rèn)為沒(méi)有修改過(guò),將跳過(guò)锣光;
聲明task的inputs和outputs
Gradle通過(guò)比較2個(gè)構(gòu)建task的inputs和outputs來(lái)決定是否是最新的;當(dāng) inputs和outputs不同時(shí)铝耻,task才運(yùn)行誊爹,否則跳過(guò);
輸入可以是一個(gè)目錄瓢捉,文件或?qū)傩缘绕登穑粋€(gè)task的輸出是通過(guò)一個(gè)目錄或1~n個(gè)文件來(lái)定義的;
inputs與outputs在DefaultTask
類中泡态,被定義為屬性或者直接子類來(lái)表示搂漠;
創(chuàng)建Task用來(lái)發(fā)布產(chǎn)品版本,并自動(dòng)修改配置文件
// 產(chǎn)品發(fā)布版本
task makeReleaseVersion(group: 'versioning', description: 'Makes project a reelase version.') << {
version.release = true
// ant task 的propertyfile 提供便利的方式修改屬性文件 (version.properties)
ant.propertyfile(file: versionFile) {
entry(key: 'release', type: 'string', operation: '=', value: 'true')
}
}
通過(guò)運(yùn)行 :gralde makeReleaseVersion
可以發(fā)現(xiàn)配置文件某弦,確實(shí)修改了桐汤;
但Gradle并不知道 我們的 是發(fā)布版本而克,為了解決,需要聲明它的inputs與outputs
task makeReleaseVersion(group: 'versioning', description: 'Makes project a reelase version.') {
// 在配置階段聲明 inputs/outputs
inputs.property('release', version.release) // 聲明版本的release屬性作為輸入
outputs.file versionFile // 由于版本文件被修改怔毛,所以他被聲明最為輸出文件屬性
doLast { // 閉包為動(dòng)作代碼
version.release = true
ant.propertyfile(file: versionFile) {
entry(key: 'release', type: 'string', operation: '=', value: 'true')
}
println(">>>>>> makeReleaseVersion")
}
}
task的inputs和outputs是在配置階段執(zhí)行的用來(lái)連接task依賴员萍,需要在配置塊中被定義,而且需要確保賦給 inputs和outputs的值在配置階段是可訪問(wèn)的拣度;
如果需要編程獲得輸出碎绎,可通過(guò)TaskOutputs上的upToDateWhen(Closure)
方法實(shí)現(xiàn),此方法是在執(zhí)行階段執(zhí)行的抗果;如果閉包返回true筋帖,則認(rèn)為該task時(shí)最新的;
如果2次執(zhí)行 gradle makeReleaseVersion
后一次gradle知道項(xiàng)目版本被設(shè)置為發(fā)布版本了冤馏,并自動(dòng)跳過(guò)第二次執(zhí)行日麸;
使用自定義Task
自定義Task包含2個(gè)組件:自定義的task類(封裝邏輯行為)與真實(shí)的task(提供用于配置行為的task類所暴露的屬性值);gradle稱之為增強(qiáng)型task宿接;
可維護(hù)性是編寫(xiě)自定義task類的優(yōu)勢(shì)之一赘淮,畢竟操作的是一個(gè)實(shí)際的類;
編寫(xiě)自定義Task
繼承自 DefaultTask
睦霎,使用注解表示輸入輸出梢卸;
class ReleaseVersionTask extends DefaultTask {
@Input Boolean release // 注解聲明輸入屬性 release
@OutputFile File destFile // 定義輸出文件
ReleaseVersionTask() {
group = 'versioning'
description = 'Makes project a realase version.'
}
// 使用注解聲明將被執(zhí)行的方法 ,
// 加上這個(gè)action的作用是當(dāng)執(zhí)行這個(gè)task的時(shí)候會(huì)自動(dòng)執(zhí)行這個(gè)方法
@TaskAction
void start() {
project.version.release = true
ant.propertyfile(file: versionFile) {
entry(key: 'release', type: 'string', operation: '=', value: 'true')
}
}
}
使用自定義Task
在構(gòu)建腳本中副女,創(chuàng)建一個(gè)ReleaseVersionTask
類型的task蛤高,并且通過(guò)它的屬性賦值來(lái)設(shè)置輸入和輸出,如:
// 增強(qiáng)型task暴露的屬性單獨(dú)賦值
task makeReleaseVersion2(type: ReleaseVersionTask) {
release = version.relase
destFile = versionFile
}
task規(guī)則
某些時(shí)候碑幅,編寫(xiě)的task可能做著相同的事情戴陡,如下版本的管理task任務(wù):
// 主版本+1
task incrementMajorVersion(group: 'versioning', description: 'Increments project major version.') << {
String currentVersion = version.toString()
++version.major
String newVersion = version.toString()
logger.info "Incrementing major project version: $currentVersion -> $newVersion"
ant.propertyfile(file: versionFile) {
entry(key: 'major', type: 'int', operation: '+', value: 1)
}
}
// 次版本+1
task incrementMinorVersion(group: 'versioning', description: 'Increments project major version.') << {
String currentVersion = version.toString()
++version.minor
String newVersion = version.toString()
logger.info "Incrementing minor project version: $currentVersion -> $newVersion"
ant.propertyfile(file: versionFile) {
entry(key: 'minor', type: 'int', operation: '+', value: 1)
}
}
分別運(yùn)行命令:gradle incrementMinorVersion -i
,可以看到輸出沟涨;
但上面的是否可以改進(jìn)呢恤批?
task規(guī)則的命名模式
根據(jù)task名稱模式執(zhí)行特定的邏輯,模式由2部分組成:
task名稱的靜態(tài)部分 與 一個(gè)占位符裹赴,他們聯(lián)合起來(lái)組成一個(gè)命令喜庞;
如上面的,像:increment<Classifier>Version
Gralde的一些核心插件棋返,利用了task規(guī)則延都,如:clean<TaskName>;
聲明task規(guī)則
首先需要獲得對(duì)TaskContainer
的引用,然后可調(diào)用其addRule(String, Closure)
方法睛竣,通過(guò)project的getTasks()方法獲取 TaskContainer引用晰房;
使用task規(guī)則,實(shí)現(xiàn)上線動(dòng)作:
// 使用 taskContainer 添加規(guī)則
tasks.addRule("Pattern: increment<Classifier>Version - Increments the project version.") {
String taskName ->
if (taskName.startsWith('increment') && taskName.endsWith('Version')) {
task(taskName) << { // 符合命名模式的task動(dòng)態(tài)添加doLast方法
String classifier = (taskName - 'increment' - 'Version'.toLowerCase()) // 提取
String currentVersion = version.toString()
switch (classifier) {
case 'major': ++version.major
break
case 'minor': ++version.minor
break
default: throw new GradleException("Invalid version type '$classifier', Allowed " +
" types: ['Major','Minor']")
}
String newVersion = version.toString()
logger.info "Incrementing $classifier project version: $currentVersion -> $newVersion"
ant.propertyfile(file: versionFile) {
entry(key: classifier, type: 'int', operation: '+', value: 1)
}
}
}
}
通過(guò)運(yùn)行gradle tasks
會(huì)列出一個(gè)具體的tasks組rules:
task 規(guī)則不能分組,其顯示在 Rules組下殊者;
我們運(yùn)行命令:gradle incrementMajorVersion -i
与境,輸出如下:
在buildSrc目錄下構(gòu)建代碼
可以將構(gòu)建腳本的一些groovy類:如上面的ProjectVersion和自定義的task ReleaseVersionTask,這些適合移動(dòng)到項(xiàng)目的 buildSrc
目錄下幽污;
Gradle在buildSrc目錄下使源文件結(jié)構(gòu)標(biāo)準(zhǔn)化嚷辅,Java代碼在 src/main/java
,Groovy代碼放在src/main/groovy
目錄下;這些目錄下代碼會(huì)自動(dòng)編譯距误,并添加到Gradle構(gòu)建腳本的classpath中簸搞;
Android Studio 中在項(xiàng)目的根目錄,建立buildSrc文件夾准潭,并sync一下工程趁俊,然后新增文件夾路徑 src/main/groovy/包名, 就可以創(chuàng)建groovy腳本了;
在當(dāng)前 module下對(duì)應(yīng)的 build.gradle文件刑然,直接引入 buildSrc 下的類寺擂,這樣Task定義與build.gradle分離了;
import com.better.ProjectVersion2
import com.better.ReleaseVersionTask2
總結(jié):
了解一下Task的工作細(xì)節(jié)泼掠、自定義Task怔软,也了解一些Task配置與Task動(dòng)作之間的區(qū)別;最后我們?cè)赽uildSrc中择镇,實(shí)現(xiàn)自定義Task的分離挡逼,將task獨(dú)立出來(lái);