前一段時間看到不少人在技術(shù)論壇里問「剛學(xué) Android 不久光稼,對 Gradle 不懂沉唠,看了很多資料依然一知半解」奏赘,一時手癢對Gradle做了一些入門的整理竹揍,希望對大家有幫助!
說到Gradle 痕鳍,Gradle是一種構(gòu)建工具硫豆,它拋棄了基于XML的構(gòu)建腳本,取而代之的是采用一種基于Groovy的內(nèi)部領(lǐng)域特定語言笼呆。近期册舞,Gradle獲得了極大的關(guān)注宙刘,這也是我決定去研究Gradle的原因搪锣。
這篇教程講授了三部分內(nèi)容:
簡單了解一下Gradle的基礎(chǔ)Groovy 語言
理解Gradle構(gòu)建的一些基本知識
理解Gradle在實際過程中的用途及應(yīng)用
一. Groovy
Groovy 是沒有類型的 Java 代碼 ,語法更簡潔被饿,形式有點類似腳本語言,是被gradle用于構(gòu)建腳本的語言
(一)特殊類型
1铭若、范圍
范圍 是一系列的值洪碳。例如 “0..4” 表明包含 整數(shù) 0、1奥喻、2偶宫、3、4环鲤。Groovy 還支持排除范圍纯趋,“0..<4” 表示 0、1、2吵冒、3纯命。還可以創(chuàng)建字符范圍:“a..e” 相當(dāng)于 a、b痹栖、c亿汞、d、e揪阿×莆遥“a..
def range = 0..4
2、集合
def coll = ["Groovy", "Java", "Ruby"]
3南捂、映射
def hash = [name:"Andy", "VPN-#":45]
(二)閉包Closure
閉包是可執(zhí)行的代碼塊吴裤。它們不需要名稱,可以在定義之后執(zhí)行溺健。
1麦牺、 閉包的格式
def xxx = {paramters -> code}?//或者
def xxx = {無參數(shù),純code} 這種case不需要->符號
2鞭缭、 閉包的特點
閉包在Groovy中大量使用剖膳,比如很多類都定義了一些函數(shù),這些函數(shù)最后一個參數(shù)都是一個閉包岭辣。
Groovy中吱晒,當(dāng)函數(shù)的最后一個參數(shù)是閉包的話,可以省略圓括號易结。比如
其實標(biāo)識doLast為一個函數(shù)枕荞,函數(shù)的最后一個參數(shù)為閉包
doLast({
println'Hello world!'
})
有了圓括號,你會知道 doLast只是把一個Closure對象傳了進(jìn)去搞动。很明顯,它不代表這段腳本解析到doLast的時候就會調(diào)用println 'Hello world!' 渣刷。
但是把圓括號去掉后鹦肿,就感覺好像println 'Hello world!'立即就會被調(diào)用一樣!
舉例:閉包用于迭代
def acoll = ["Groovy", "Java", "Ruby"]
acoll.each{
println it
}
閉包中的it變量是一個關(guān)鍵字辅柴,指向被調(diào)用的外部集合的每個值 — 它是默認(rèn)值箩溃,可以用傳遞給閉包的參數(shù)覆蓋它。
(三)groovy腳本的實質(zhì)
既然是基于Java的碌嘀,Groovy會先把xxx.groovy中的內(nèi)容轉(zhuǎn)換成一個Java類涣旨。比如:
test.groovy的代碼是:
println?'Groovy world!'
Groovy把它轉(zhuǎn)換成這樣的Java類:
執(zhí)行groovyc-d classes test.groovy
groovyc是groovy的編譯命令,-d classes用于將編譯得到的class文件拷貝到classes文件夾下
二.Gradle
Gradle是一種依賴管理工具股冗,基于Groovy語言霹陡,面向Java應(yīng)用為主,它拋棄了基于XML的各種繁瑣配置,取而代之的是一種基于Groovy的內(nèi)部領(lǐng)域特定(DSL)語言烹棉。 用于自動化構(gòu)建攒霹、測試、發(fā)布打包浆洗。
(一)基本組件
Gradle是一個框架催束,它定義一套自己的游戲規(guī)則。我們要玩轉(zhuǎn)Gradle伏社,必須要遵守它設(shè)計的規(guī)則抠刺。下面我們來講講Gradle的基本組件:
Gradle中,每一個待編譯的工程都叫一個Project摘昌。每一個Project在構(gòu)建的時候都包含一系列的Task速妖。比如一個Android APK的編譯可能包含:Java源碼編譯Task、資源編譯Task第焰、JNI編譯Task买优、lint檢查Task、打包生成APK的Task挺举、簽名Task等杀赢。
1. gradle projects查看工程信息
到目前為止,我們了解了Gradle什么呢湘纵?
每一個Project都必須設(shè)置一個build.gradle文件脂崔。至于其內(nèi)容,我們留到后面再說梧喷。
對于multi-projects build砌左,需要在根目錄下也放一個build.gradle,和一個settings.gradle铺敌。
一個Project是由若干tasks來組成的汇歹,當(dāng)gradlexxx的時候,實際上是要求gradle執(zhí)行xxx任務(wù)偿凭。這個任務(wù)就能完成具體的工作产弹。
2. gradle tasks查看任務(wù)信息
gradleproject-path:tasks就行。注意弯囊,project-path是目錄名痰哨,后面必須跟冒號。
對于Multi-project匾嘱,在根目錄中斤斧,需要指定你想看哪個poject的任務(wù)。不過你要是已經(jīng)cd到某個Project的目錄了霎烙,則不需指定Project-path撬讽。
(二)gradle build的生命周期
Gradle有一個初始化流程蕊连,這個時候settings.gradle會執(zhí)行。
在配置階段锐秦,每個Project都會被解析咪奖,其內(nèi)部的任務(wù)也會被添加到一個有向圖里,用于解決執(zhí)行過程中的依賴關(guān)系酱床。
然后才是執(zhí)行階段羊赵。你在gradle xxx中指定什么任務(wù),gradle就會將這個xxx任務(wù)鏈上的所有任務(wù)全部按依賴順序執(zhí)行一遍扇谣!
(1)生命周期
eg:Single project build example
settings.gradle
println 'This is executed during the initialization phase.'
build.gradle
println 'This is executed during the configuration phase.'
task configured {
println 'This is also executed during the configuration phase.'
}
task test << {
println 'This is executed during the execution phase.'
}
task testBoth {
doFirst {
println 'This is executed first during the execution phase.'
}
doLast {
println 'This is executed last during the execution phase.'
}
println 'This is executed during the configuration phase as well.'
}
(2)setting.gradle中多工程配置
其中setting.gradle在多project的配置中昧捷,用于聲明包含的project及其層級關(guān)系(平坦和層級結(jié)構(gòu)的project都支持)
include 'project1', 'project2:child', 'project3:child1'
includeFlat 'project3', 'project4'
(3)根據(jù)生命周期定制task
eg:Logging of start and end of each task execution
build.gradle
task ok
task broken(dependsOn: ok) << {
thrownew RuntimeException('broken')
}
gradle.taskGraph.beforeTask { Task task ->
println "executing $task ..."
}
gradle.taskGraph.afterTask { Task task, TaskState state ->
if (state.failure) {
println "FAILED"
}
else {
println "done"
}
}
三.Gradle用途
(1)依賴管理
我們的project可能會用到其他的庫(本地、maven或者ivy)
eg:Declaring dependencies
本地project依賴
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile project(':bugrpt')
}
遠(yuǎn)程庫依賴
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile group: 'org.hibernate', name: 'hibernate-core', version: '3.6.7.Final'
testCompile group: 'junit', name: 'junit', version: '4.+'
}
標(biāo)識project需要hibernate-core一起編譯罐寨,project test需要junit的編譯靡挥,另外一種寫法group:name:version
dependencies {
compile 'org.hibernate:hibernate-core:3.6.7.Final'
}
像這些外部的庫,gradle是通過repository來找到的
eg:Usage of a remote Maven repository
build.gradle
repositories {
maven {
url "http://repo.mycompany.com/maven2"
}
}
eg:Usage of a remote Maven repository
build.gradle
repositories {
maven {
url "http://repo.mycompany.com/maven2"
}
}
repositories {
maven {
url "http://repo.mycompany.com/maven2"
}
}repositories {
ivy {
// URL can refer to a local directory
url "../local-repo"
}
}
(2)版本發(fā)布
可以將自己的lib工程發(fā)布到maven、jcenter等倉庫供其他開發(fā)者使用
eg:發(fā)布工程
uploadArchives {
repositories {
ivy {
credentials {
username "username"
password "pw"
}
url "http://repo.mycompany.com"
}
}
}
(3) 差異管理
比如app生成不同版本(免費鸯绿,收費)跋破,適配特殊機型,多渠道等需要發(fā)多個包瓶蝴,最終能編譯出的apk的數(shù)量是由productflavor與BuildType決定的毒返,BuildType默認(rèn)有debug和release兩種
eg:免費與收費設(shè)置不同包名
productFlavors {
pay {
applicationId "me.ghui.gradledemo.pay"
}
free {}
}
eg:buildTypes 對應(yīng)的不同版本
android {
buildTypes {
debug {
applicationIdSuffix ".debug"
}
jnidebug {
initWith(buildTypes.debug)
packageNameSuffix ".jnidebug"
jniDebuggable true
}
}
}
對于每一種buildTypes會創(chuàng)建相應(yīng)的ssemble任務(wù),比如debug會自動創(chuàng)建assembleDebug任務(wù)
創(chuàng)建一個新的的Build Types非常簡單舷手,只需要在buildTypes下面通過調(diào)用initWith或者使用閉包添加一個新的元素拧簸。下表是可以配置的屬性以及默認(rèn)值:
屬性明 debug版本默認(rèn)值release或其他版本默認(rèn)值
debuggable true false
jniDebuggable false false
renderscriptDebuggable false false
renderscriptOptimLevel 3 3
applicationIdSuffix null null
versionNameSuffix null null
signingConfig android.signingConfigs.debug null
zipAlignEnabled false true
minifyEnabled false false
proguardFile N/A (set only) N/A (set only)
proguardFiles N/A (set only) N/A (set only)
eg:多渠道打包
附錄 gradle腳本基礎(chǔ)
1 project和task
task表示構(gòu)建的一次原子操作,包括編譯類男窟、創(chuàng)建jar包盆赤、發(fā)布到repository、生成javadoc等歉眷,而project通常包含多個task
task hello {
doLast {
println 'Hello world!'
}
}
這個腳本定義了一個叫做hello的task牺六,并且添加了一個action,這個action實際上是由groovy語言編寫的閉包汗捡,更簡潔的寫法
task hello << {
println 'Hello world!'
}
> gradle -q hello
Hello world!
2 task依賴
project('projectA') {
task taskX(dependsOn: ':projectB:taskY') << {
println 'taskX'
}
}
project('projectB') {
task taskY << {
println 'taskY'
}
}
3 可以動態(tài)創(chuàng)建task兔乞,并通過api操作task
4.times { counter ->
task "task$counter" << {
println "I'm task number $counter"
}
}
task0.dependsOn task2, task3
4 task可以定義屬性
task myTask {
ext.myProperty = "myValue"
}
task printTaskProperties << {
println myTask.myProperty
}
5 設(shè)置默認(rèn)
defaultTasks 'clean', 'run'
task clean << {
println 'Default Cleaning!'
}
task run << {
println 'Default Running!'
}
task other << {
println "I'm not a default task!"
}
6 可以添加HOOK
task distribution << {
println "We build the zip with version=$version"
}
task release(dependsOn: 'distribution') << {
println 'We release now'
}
gradle.taskGraph.whenReady {taskGraph ->
if (taskGraph.hasTask(release)) {
version = '1.0'
} else {
version = '1.0-SNAPSHOT'
}
}
7 定位task
1)使用task的名字
println hello.name
2)使用tasks collection
println tasks.hello.name
3)使用project:task路徑定位
tasks.getByPath(':projectA:hello').path
8 配置task
Configuring a task - with closure
task copy(type: Copy) {
description 'Copies the resource directory to the target directory.'
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}