本篇主要是個(gè)人學(xué)習(xí)gradle的筆記總結(jié)
一.開(kāi)始之前
1. 為什么學(xué)習(xí)Gradle
- 采用DSL(Domain Specific Language)語(yǔ)言來(lái)描述和控制構(gòu)建邏輯。(所謂領(lǐng)域?qū)S谜Z(yǔ)言,其基本思想是“求專不求全”,不像通用目的語(yǔ)言那樣目標(biāo)范圍涵蓋一切軟件問(wèn)題,而是專門針對(duì)某一特定問(wèn)題的計(jì)算機(jī)語(yǔ)言。)
- 支持Maven或者Ivy的依賴管理。
- 插件可以提供自己的DSL和API供文件使用。
- 讓代碼或資源復(fù)用更容易。
- 讓創(chuàng)建同一應(yīng)用程序的不同版本變得更加容易,無(wú)論是多個(gè) apk 發(fā)布版本還是同一個(gè)應(yīng)用的不同定制版本。
2. 學(xué)習(xí)條件
由于Android最新推薦的編譯方式是采用Gradle進(jìn)行編譯,因此我們必須學(xué)習(xí)Gradle的基本知識(shí), 而Gradle是基于Groovy語(yǔ)法編寫的,因此我們必須了解Groovy語(yǔ)法 ,而Groovy又是基于Java語(yǔ)言的,是java語(yǔ)言的擴(kuò)展, 所以說(shuō)只要我們會(huì)java語(yǔ)言就可以編寫出Gradle腳本。
3. 學(xué)習(xí)目標(biāo)
A: 學(xué)習(xí)Groovy基本概念, 學(xué)習(xí)Groovy集合, 類,和閉包語(yǔ)法。
B: 學(xué)習(xí)Gradle 基本用法, 能夠編寫task,理解project概念。
C: 可以理解android的編譯配置。
二.Groovy初探
1. 什么是Groovy
Grovvy是JVM的一個(gè)替代語(yǔ)言, 是指可以用Groovy在Java平臺(tái)上進(jìn)行Java編程,使用方式基本和Java代碼先同,它也是Java的擴(kuò)展。
2. 開(kāi)發(fā)環(huán)境配置
http://www.groovy-lang.org/learn.html
配置方式如下:

3. Groovy的特性
Groovy松散的java語(yǔ)法允許省略分號(hào)和修飾符;除非另指定,groovy所有內(nèi)容都為public;允許定義簡(jiǎn)單腳本,無(wú)需定義正規(guī)的class對(duì)象;允許省略變量類型。
4. Groovy和java的對(duì)比
http://www.groovy-lang.org/differences.html
如下:讀取文件
Path file = Paths.get("/path/to/file");
Charset charset = Charset.forName("UTF-8");
try (BufferedReader reader = Files.newBufferedReader(file, charset)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
can be written like this:
new File('/path/to/file').eachLine('UTF-8') {
println it
}
or, if you want a version closer to Java:
new File('/path/to/file').withReader('UTF-8') {
reader -> reader.eachLine {
println it
}
}
5. Groovy集合,閉包,映射
范圍 :for (i in 0..4) 將包含的范圍限制在0-4間。集合。def coll = ["Groovy", "Java", "Ruby"] coll << “smalltalk”映射 def hash = [“name”:”andy”, “vip”:”45”]閉包 coll.each{ println it} 或 coll.each{value -> println value}
如果閉包沒(méi)有定義參數(shù),那么隱含一個(gè)參數(shù)it, 和this作用類似。
對(duì)于閉包的參數(shù)需要查看api文檔。
6. 類
Groovy類就是java類,初始化時(shí)Groovy 自動(dòng)提供一個(gè)構(gòu)造函數(shù),構(gòu)造函數(shù)接受一個(gè)名稱-值對(duì)的映射,這些名稱-值對(duì)與類的屬性相對(duì)應(yīng)。這是 Groovy 的一項(xiàng)開(kāi)箱即用的功能 — 用于類中定義的任何屬性,Groovy 允許將存儲(chǔ)了大量值的映射傳給構(gòu)造函數(shù)。還會(huì)對(duì)成員變量定義get,set方法,所以可以直接點(diǎn)引用。
7. 看具體例子。
Demo地址:
Groovy API文檔為: http://www.groovy-lang.org/api.html
三.Groovy深入
1. Groovy和JVM的關(guān)系
除了語(yǔ)言和Java相通外,Groovy有時(shí)候又像一種腳本語(yǔ)言。當(dāng)我執(zhí)行Groovy腳本時(shí),Groovy會(huì)先將其編譯成Java類字節(jié)碼,然后通過(guò)Jvm來(lái)執(zhí)行這個(gè)Java類。

實(shí)際上,由于Groovy Code在真正執(zhí)行的時(shí)候已經(jīng)變成了Java字節(jié)碼,所以JVM根本不知道自己運(yùn)行的是Groovy代碼.
2. 腳本和類
既然是基于java來(lái)執(zhí)行的,那我們將groovy轉(zhuǎn)會(huì)為java類。
執(zhí)行 groovyc-d classes test.groovy
groovyc是groovy的編譯命令,-d classes用于將編譯得到的class文件拷貝到classes文件夾下。
我們可以看到helloworld.groovy 被轉(zhuǎn)化為java class類,繼承自Script。
Song 類被轉(zhuǎn)化為實(shí)現(xiàn)GroovyObject的類,并封裝了get,set方法。
可以看出, groovy既可以作為類來(lái)使用, 又可以作為腳本來(lái)使用。
Groovy 腳本的代碼其實(shí)都會(huì)被放到run函數(shù)中執(zhí)行的。變量作用域,區(qū)分def和不用def定義, def定義的為run函數(shù)作用域,無(wú)def的為全局屬性,細(xì)節(jié)可以看字節(jié)碼。
3. IO操作
def targetFile = new File(xxx)
targetFile.eachLine{ line ->
println line
}
直接讀取文件: targetFile.getBytes()
獲取輸入流: def is = targetFile.newInputStream() is.close
閉包輸入流:
targetFile.withInputStream{ is ->
操作is, 無(wú)需關(guān)閉輸入流, 閉包會(huì)自動(dòng)關(guān)閉。
}
寫文件:
def srcFile = new File(源文件)
def targetFile = new File(目標(biāo)文件)
targetFile.withOutputStream{os ->
srcFile.withInputStream{ is ->
os << is
}
}
這里重載了<< 操作符, 細(xì)節(jié)可以查看api文檔,方法名為leftShift
四.Gradle初探
1. 什么是Gradle
用戶手冊(cè):https://docs.gradle.org/current/userguide/userguide.html
API文檔:https://docs.gradle.org/current/javadoc/
DSL手冊(cè):https://docs.gradle.org/current/dsl/
Gradle 是配置編譯腳本, 也是編程開(kāi)發(fā)框架。
Gradle 由一個(gè)或多個(gè)proejct組成, 每一個(gè)待編譯的工程都叫一個(gè)project,每一個(gè)project在構(gòu)建的時(shí)候都包含一系列的task。比如一個(gè)Android apk的編譯可能包含:java源碼編譯Task,資源編譯Task,JNI編譯Task,lint檢查Task, 打包生成APK的task,簽名Task等等。一個(gè)project到底包含多少Task,其實(shí)是由編譯腳本指定的插件決定。插件就是用來(lái)定義Task,并且具體執(zhí)行這些task的東西。
2. 開(kāi)發(fā)環(huán)境配置
http://gradle.org/ 下載對(duì)應(yīng)的gradle文件到本地,配置環(huán)境變量即可。
GRADLE_HOME=/Users/zhangyuqiang/work/soft/gradle-2.2.1;
加到~/.bash_profile 中, export GRADLE_HOME即可.
下面Demo地址:https://github.com/davenkin/gradle-learning
3. Task
Task是gradle中的一種數(shù)據(jù)類型,它代表了一些藥執(zhí)行或者要干的工作。每一個(gè)task都要和一個(gè)project關(guān)聯(lián)。
創(chuàng)建一個(gè)task
task hello {
doLast{
println “hello gradle”
}
}
或者
task hello << {
println “hello gradle”
}
參考Demo: 2-define-task, 3-undertand-gradle-syntax,
4. Project
每一個(gè)build.gradle 就是一個(gè)project。一個(gè)project中會(huì)有一個(gè)或多個(gè)task。
5. 屬性
(Demo: 5-define-custom-properties)
gradle 的project中會(huì)有一些默認(rèn)的屬性,可以理解為類的成員變量,我們還可以給project增加一些額外屬性, 通過(guò)ext。
6. 依賴關(guān)系
(Demo: 7-dependency-management, 8-multi-project)
project依賴可以通過(guò)dependencies{}來(lái)設(shè)置依賴,task依賴可以通過(guò)dependsOn 來(lái)設(shè)置。
7. Multi-Project組織
(Demo: 8-multi-project)
多project管理,主要依靠settings.gradle ,在該文件中include 需要編譯的模塊名即可。可以在該文件中增加函數(shù),如initGradleEnvironment(),這些函數(shù)會(huì)在gradle構(gòu)建整個(gè)工程任務(wù)的時(shí)候執(zhí)行。其實(shí)include也是函數(shù)。
8. Gradle命令
gradle projects
gradle tasks
gradle taskname 執(zhí)行任務(wù)
gradle clean, assembleDebug, aR, build …
五.Gradle深入
1. Gradle工作流程

首先是初始階段,對(duì)于multi-project而言,就是settings.gradle配置執(zhí)行。
其次是配置階段,該階段解析每個(gè)project中的build.gradle。這兩個(gè)階段我們可以增加hook進(jìn)來(lái),執(zhí)行相關(guān)任務(wù)。
最后是執(zhí)行階段,執(zhí)行完成后我們也可以增加hook。
Gradle基于groovy,所以編譯執(zhí)行時(shí)gradle也會(huì)把腳本轉(zhuǎn)化為java對(duì)象。
Gradle中主要有三種對(duì)象, 這三種對(duì)象和三種不同的腳本文件對(duì)應(yīng), 在Gradle執(zhí)行的時(shí)候,會(huì)將腳本轉(zhuǎn)化成對(duì)應(yīng)的對(duì)象,分別為Gradle對(duì)象,Project對(duì)象,Settings對(duì)象。
2. Gradle編程模型及生命周期
https://docs.gradle.org/current/userguide/build_lifecycle.html
3. Gradle對(duì)象
當(dāng)我們執(zhí)行g(shù)radle xxx時(shí),Gradle會(huì)從默認(rèn)的配置腳本中構(gòu)造出一個(gè)Gradle對(duì)象, 在整個(gè)執(zhí)行過(guò)程中只有這么一個(gè)對(duì)象, Gradle對(duì)象的數(shù)據(jù)類型就是Gradle。我們一般很少去定制這個(gè)腳本
4. Settings對(duì)象
settings.gradle 會(huì)被轉(zhuǎn)化為一個(gè)Settings對(duì)象。
5. Project對(duì)象
每個(gè)build.gradle 都會(huì)轉(zhuǎn)化為一個(gè)Project對(duì)象。由于project對(duì)應(yīng)到具體工程, 因此要為project加載對(duì)應(yīng)的插件。其實(shí)每一個(gè)project具體包含多少個(gè)task是由插件決定的。
6. 代理機(jī)制
Gradle大量地使用了Groovy閉包的delegate機(jī)制。簡(jiǎn)單來(lái)說(shuō),delegate機(jī)制可以使我們將一個(gè)閉包中的執(zhí)行代碼的作用對(duì)象設(shè)置成任意其他對(duì)象。3-undertand-gradle-syntax。
apply方法, 使用: apply plugin: ‘com.android.library’, 如果編譯lib,則使用此插件, 也可以加載一個(gè)gradle文件,如: apply from : ‘utils.gradle’。
此處可以u(píng)tils.gradle定義的屬性和方法。為什么可以使用呢? 我們知道gradle和groovy一樣,每個(gè)腳本都繼承自Script。utils.gradle 也會(huì)被轉(zhuǎn)化為一個(gè)Script對(duì)象, Script對(duì)象中有一個(gè)delegate對(duì)象, 在apply函數(shù)中有個(gè)from參數(shù),還有個(gè)to參數(shù), 通過(guò)to參數(shù)可以將delegate對(duì)象指向別的東西,這里即utils 的Scrpit類, 當(dāng)你在自己的Script中操作一些不是自己定義的變量或者函數(shù)時(shí),gradle會(huì)到Script的delegate對(duì)象中去找。
7. BuildScriptBlock

六.Android中g(shù)radle基本配置
https://developer.android.com/intl/zh-cn/tools/building/plugin-for-gradle.html
http://google.github.io/android-gradle-dsl/current/
1. 插件

2. 各Script配置
android中的BuildScriptBlock

3. gradle命令及依賴配置
依賴配置有:模塊依賴,本地依賴,遠(yuǎn)程依賴。

命令:頂級(jí)命令有4個(gè)
assemble Builds the project output.
Check Runs checks and tests.
Build Runs both assemble and check.
Clean Performs the clean
常用的有g(shù)radle assembleDebug 編譯debug包。
Gradle assembleRelease, 編譯release包, 縮寫為gradle aR.
4. multi-dex 配置
https://developer.android.com/intl/zh-cn/tools/building/multidex.html
七.Android中g(shù)radle的高級(jí)配置
1. 構(gòu)建變種版本-BuidVariant。
構(gòu)建類型+定制產(chǎn)品=構(gòu)建變種版本
BuildType + ProductFlavor 任何一種組合都會(huì)是一個(gè)版本。
http://wiki.jikexueyuan.com/project/android-gradle-guide/build-variants.html
BuildType : 可以分為debug, release, publish , product, demo 等等。
ProductFlavor:可自定義為flavor1,flavor2…
android {
...
defaultConfig {
minSdkVersion 8
versionCode 10
}
productFlavors {
flavor1 {
packageName "com.example.flavor1"
versionCode 20
}
flavor2 {
packageName "com.example.flavor2"
minSdkVersion 14
}
}
}
每一個(gè)Variant也會(huì)創(chuàng)建額外的sourceSet:
android.sourceSets.flavor1Debug
位于src/flavor1Debug/
android.sourceSets.flavor1Release
位于src/flavor1Release/
android.sourceSets.flavor2Debug
位于src/flavor2Debug/
android.sourceSets.flavor2Release
位于src/flavor2Release/
這些sourceSet擁有比Build Type的sourceSet更高的優(yōu)先級(jí),并允許在Variant的層次上做一些定制。
2. 高級(jí)構(gòu)建選項(xiàng)
android {
aaptOptions {
noCompress 'foo', 'bar'
ignoreAssetsPattern "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"
}
}
aapt操作配置將應(yīng)用到所有task上。
3. 操作Task
Android項(xiàng)目中會(huì)有大量的task, 并且他們基于buildType和productFlavor生成的task, 從而我們直接引用到該task的編譯成java后的class,為了解決該問(wèn)題, android對(duì)象提供了三個(gè)Collection屬性:
applicationVariants(只適用于app plugin)
libraryVariants(只適用于library plugin)
testVariants(兩個(gè)plugin都適用)
這三個(gè)collection中的任意一個(gè)都會(huì)觸發(fā)生成所有對(duì)應(yīng)的task。
android.applicationVariants.each { variant ->
....
}
http://wiki.jikexueyuan.com/project/android-gradle-guide/advanced-build-customization.html
通過(guò)這些變量和方法可定制對(duì)應(yīng)的功能。
官方詳細(xì)文檔:http://tools.android.com/tech-docs/new-build-system/user-guide
八.Gradle自定義Task和Plugin
Gradle本身只是一個(gè)架子,真正起作用的是Task和Plugin。要真正了解Task和Plugin的工作機(jī)制并熟練運(yùn)用,學(xué)會(huì)自定義Task類型和Plugin是大有裨益的。
1. 自定義Task。
Gradle中的Task要么是由不同的Plugin引入的,要么是我們自己在build.gradle文件中直接創(chuàng)建的。在默認(rèn)情況下,我們所創(chuàng)建的Task是DefaultTask類型,該類型是一個(gè)非常通用的Task類型,而在有些時(shí)候,我們希望創(chuàng)建一些具有特定功能的Task,比如Copy和Jar等。
Demo: 9-custom-task, 分為三部分,1,在build.gradle中直接定義Task,2.在當(dāng)前工程中定義Task,3.在單獨(dú)的項(xiàng)目中定義Task,將其上傳maven庫(kù)中,其他項(xiàng)目來(lái)引用。
2. 自定義Plugin。
在Plugin中,我們可以向Project中加入新的Task,定義configurations和property等。我們3種方法可以自定義Plugin,這些方法和自定義Task類型的3種方法相似。
Demo:10-custom-plugin 每一個(gè)自定義的Plugin都需要實(shí)現(xiàn)Plugin<T>接口,事實(shí)上,除了給Project編寫Plugin之外,我們還可以為其他Gradle類編寫Plugin。該接口定義了一個(gè)apply()方法,在該方法中,我們可以操作Project,比如向其中加入Task,定義額外的Property等。
九.通過(guò)Gradle發(fā)布jar,aar,plugin到JCenter和Maven Central
1. 發(fā)布到j(luò)center中
http://www.cnblogs.com/qianxudetianxia/p/4322331.html
2. 發(fā)布到maven central
http://my.oschina.net/specialcyci/blog/371352#OSC_h3_2
3. 發(fā)布到本地maven庫(kù)
apply plugin:‘maven’
uploadArchives{
repositories.mavenDeployer{
repository(url: ‘file:../lib’)
}
}
使用時(shí)
buildScript{
repositories{
maven{
url ‘file:../lib’}}}
十. 新的構(gòu)建方案
facebook buck https://buckbuild.com/http://www.voidcn.com/blog/u014077888/article/p-4146683.html
十一. 例子
helloworld.groovy
//1. 打印helloworld
println 'helloworld'
//2. 類型定義
def value = "hello"
println value
//3. 查看類型是什么
println value.class
def value1 = 4
println value1.class
def value2 = 'a'
println value2.class
//4. groovy for循環(huán),int無(wú)需定義類型
def repeat(val) {
/*for(i = 0; i < 5; i++) {
println val;
}*/
//groovy中的范圍, 0..5 是一個(gè)集合
/*for(i in 0..5) {
println val;
}*/
//將范圍改為排除
for(i in 0..<5) {
println val
}
}
repeat("helloworld")
//5. 默認(rèn)參數(shù)
def repeat1(val, repeat=5) {
for(i in 0..<repeat) {
println val
}
}
repeat1("hello", 3)
repeat1("world")
//6. groovy 集合
def coll = ["Groovy", "Java", "Ruby"]
println coll.class
assert coll instanceof Collection
assert coll instanceof ArrayList
//7. 插入符號(hào), 集合操作。
coll.add("Python")
coll << "Smalltalk"
coll[5] = "Perl"
println coll
//8. * 操作符
def upper = ["Java", "Groovy"]*.toUpperCase()
println upper
//9. 映射
def hash = [name:"Andy", "VPN-#":45]
assert hash.getClass() == java.util.LinkedHashMap
hash.put("id", 23)
assert hash.get("name") == "Andy"
//或者用. 設(shè)置 ,讀取
hash.dob = "01/29/76"
println hash.name
println hash['name']
println hash
//11. 閉包
coll.each{ println it}
coll.each{name ->
println name
}
//12. 定義閉包
def excite = {word ->
return "${word}!!!"
}
println excite('helloworld')
//默認(rèn)參數(shù)為it
def greeting = { "Hello, ${it}!" }
println greeting('hello')
//當(dāng)函數(shù)的最后一個(gè)參數(shù)是閉包的話,可以省略()
def testClosure(int a1, String b1, Closure closure) {
closure() //調(diào)用閉包
}
testClosure(3, "test", {
println 'I am a closure'
})
//如android gradle中的
/*doLast({
println'Hello world!'
})*/
///////groovy 深入//////
//查看變量作用范圍
//run作用域
/*def x = 1
def printx() {
println x
}
printx()
*/
//全局作用域
y = 1
def printy() {
println y
}