導語:
隨著技術的發(fā)展,不管是前端開發(fā)、服務端開發(fā)或者是移動端開發(fā)(移動也是前端的一個分支)中都會用到自動化構(gòu)建工具。如果我們沒有使用過自動化構(gòu)建工具,可能對自動構(gòu)建工具沒有什么概念,為什么需要用到自動化構(gòu)建工具。我們先來看一下在沒有使用自動化構(gòu)建工具前,我們是如何開發(fā)項目、管理項目的。項目開發(fā)過程中涉及新建項目-》代碼開發(fā)—》依賴管理-》編譯-》測試-》打包-》上傳等過程。依賴管理,項目開發(fā)過程中會用到很多jar包,自己開發(fā)的有別人開發(fā)的,這些都放在項目的lib目錄。大點的項目幾百個,很容易造成依賴沖突,另外就是版本更新特別不方便,你得手動去復制新版本的jar包放到lib目錄下。測試過程,大家都不重視,基本上寫一個java類,在main方法調(diào)用調(diào)試幾下就完了。打包,得手動,如在Eclipse上導出jar包,web開發(fā)的話導出war包。另外發(fā)布不同版本,還得手動去更改后才能打出自己想要的。上傳過程中,得手動或用ftp上傳到服務器上面。這些過程太頻繁、瑣碎無聊,小點項目還能管理的過來,大的項目簡直是災難。技術大牛們?nèi)虩o可忍總于爆發(fā)了,發(fā)明出了構(gòu)建工具這個東西,將程序員們從水深火熱中解脫出來。
構(gòu)建工具的作用
- 依賴管理
- 自動測試、自動打包、自動發(fā)布到制定的地方去
- 機器能干的,我們絕不自己動手
Java方面主流的構(gòu)建工具
- 最早出現(xiàn)的Ant,提供了編譯、測試、打包三個最基本的功能
- 接著Maven,在Ant的基礎上加了依賴管理和發(fā)布功能,通過xml標記性語言來進行管理構(gòu)建腳步
- 接著重點來了,我們要學習的Gradle,它在Maven基礎上更近一步,使用Groovy進行管理構(gòu)建腳步,不再使用xml ,因為項目大了之后使用xml很容易造成啰唆、臃腫難以管理。而使用這種特定領域語言來管理構(gòu)建腳步具有更高的可拓展性和靈活性。
Gradle是什么
一個開源的項目自動化構(gòu)建工具,建立在Apache Ant 和 Apache Maven概念的基礎上,并引入了基于Groovy的特定領域語言(DSL),而不再使用xml形式的管理構(gòu)建腳本。
Gradle的作用,即能為我們做什么
Gradle是一個項目自動化構(gòu)建工具,它當然具備自動化構(gòu)建的所有功能,依賴管理,自動化測試、自動化打包,發(fā)布到制定的地方去。另外它具有很高的拓展性和靈活性,你想要它做什么,它都能幫你完成。
準備使用Gradle
Gradle的安裝、配置環(huán)境變量
??1. 因為Gradle是基于JVM的所以確保本地安裝了JDK java -version
??2. 打開官網(wǎng)下載安裝包,解壓到制定的目錄Gradle官網(wǎng)https://gradle.org/docs#getting-started
??3. 配置環(huán)境變量,GRADLE_HOME添加到path中,%GRADLE_HOME%\bin;
??4. 驗證是否安裝成功,gradle -v
groovy語言基礎知識
Groovy是什么
??Groovy是用Java虛擬機的一種敏捷的動態(tài)語言,它是一種成熟的面向?qū)ο缶幊陶Z言,即可用于面向?qū)ο缶幊?,又可以用作純粹的腳本語言。使用該語言不需要編寫太多的代碼,同時又具有閉包和動態(tài)語言中的其他特性。
下面的知識點都可以到官網(wǎng)文檔中查詢到http://www.groovy-lang.org/documentation.html
??與java相比
??1、Groovy完全兼容java語法,最終也編譯成字節(jié)碼
??2、Groovy注釋標記和Java一樣,支持//或者//
??3、Groovy中支持動態(tài)類型,即定義變量的時候可以不指定其類型。
??4、Groovy中,變量定義可以使用關鍵字def。注意,雖然def不是必須的,但是為了代碼清晰,建議還是使用def關鍵字。
def var =1
def str= "i am a person"
def int x = 1//也可以指定類型
5、函數(shù)定義時,參數(shù)的類型也可以不指定。比如
String function(arg1,args2){//無需指定參數(shù)類型
}
6、除了變量定義可以不指定類型外,Groovy中函數(shù)的返回值也可以是無類型的。
比如://無類型的函數(shù)定義,必須使用def關鍵字
def nonReturnTypeFunc(){
last_line //最后一行代碼的執(zhí)行結(jié)果就是本函數(shù)的返回值
}
//如果指定了函數(shù)返回類型,則可不必加def關鍵字來定義函數(shù)
String getString(){
return "I am a string"
}
其實,所謂的無返回類型的函數(shù),我估計內(nèi)部都是按返回Object類型來處理的。畢竟,Groovy是基于Java的,而且最終會轉(zhuǎn)成Java Code運行在JVM上。
??7、 函數(shù)返回值:Groovy的函數(shù)里,可以不使用return xxx來設置xxx為函數(shù)返回值。如果不使用return語句的話,則函數(shù)里最后一句代碼的執(zhí)行結(jié)果被設置成返回值。比如
//下面這個函數(shù)的返回值是字符串"getSomething return value"
def getSomething(){
"getSomething return value" //如果這是最后一行代碼,則返回類型為String
1000 //如果這是最后一行代碼,則返回類型為Integer
}
注意,如果函數(shù)定義時候指明了返回值類型的話,函數(shù)中則必須返回正確的數(shù)據(jù)類型,否則運行時報錯。如果使用了動態(tài)類型的話,你就可以返回任何類型了。
??8、Groovy對字符串支持相當強大,充分吸收了一些腳本語言的優(yōu)點:單引號''中的內(nèi)容嚴格對應Java中的String,不對$符號進行轉(zhuǎn)義
def singleQuote='I am $ dolloar' //輸出就是I am $ dolloar
雙引號""的內(nèi)容則和腳本語言的處理有點像,如果字符中有$號的話,則它會$表達式先求值。
def doubleQuoteWithoutDollar = "I am one dollar" //輸出 I am one dollar
def x = 1
def doubleQuoteWithDollar = "I am $x dolloar" //輸出I am 1 dolloar
三個引號'''xxx'''中的字符串支持隨意換行 比如
def multieLines = ''' begin
line 1
line 2
end '''
9、 Groovy語句可以不用分號結(jié)尾。Groovy為了盡量減少代碼的輸入,確實煞費苦心
??10、除了每行代碼不用加分號外,Groovy中函數(shù)調(diào)用的時候還可以不加括號。比如:
println("test") ---> println "test"
注意,雖然寫代碼的時候,對于函數(shù)調(diào)用可以不帶括號,但是Groovy經(jīng)常把屬性和函數(shù)調(diào)用混淆。比如
def getSomething(){
"hello"
}
getSomething() //如果不加括號的話,Groovy會誤認為getSomething是一個變量。
??數(shù)據(jù)類型的不同
?? Groovy中的數(shù)據(jù)類型我們就介紹兩種和Java不太一樣的:
?? 一個是Java中的基本數(shù)據(jù)類型。
?? 另外一個是Groovy中的容器類。
?? 最后一個非常重要的是閉包。
?? 基本數(shù)據(jù)類型
?? 作為動態(tài)語言,Groovy世界中的所有事物都是對象。所以,int,boolean這些Java中的基本數(shù)據(jù)類型,在Groovy代碼中其實對應的是它們的包裝數(shù)據(jù)類型。比如int對應為Integer,boolean對應為Boolean。比如下圖中的代碼執(zhí)行結(jié)果:

?? 容器類
?? List類其實是ArrayList類
變量定義:List變量由[]定義,比如
def aList = [5,'string',true] //List由[]定義,其元素可以是任何對象
變量存?。嚎梢灾苯油ㄟ^索引存取,而且不用擔心索引越界。如果索引超過當前鏈表長度,List會自動
往該索引添加元素
assert aList[1] == 'string'
assert aList[5] == null //第6個元素為空
aList[100] = 100 //設置第101個元素的值為10
assert aList[100] == 100
那么,aList到現(xiàn)在為止有多少個元素呢?
println aList.size ===>結(jié)果是101
Map類其實是LinkedHashMap類
容器變量定義
變量定義:Map變量由[:]定義,比如
def aMap = ['key1':'value1','key2':true]
Map由[:]定義,注意其中的冒號。冒號左邊是key,右邊是Value。key必須是字符串,value可以是任何對象。另外,key可以用''或""包起來,也可以不用引號包起來。比如
def aNewMap = [key1:"value",key2:true] //其中的key1和key2默認被
處理成字符串"key1"和"key2"
不過Key要是不使用引號包起來的話,也會帶來一定混淆,比如
def key1="wowo"
def aConfusedMap=[key1:"who am i?"]
aConfuseMap中的key1到底是"key1"還是變量key1的值“wowo”?顯然,答案是字符串"key1"。如果要是"wowo"的話,則aConfusedMap的定義必須設置成:
def aConfusedMap=[(key1):"who am i?"]
Map中元素的存取更加方便,它支持多種方法:
println aMap.keyName <==這種表達方法好像key就是aMap的一個成員變量一樣
println aMap['keyName'] <==這種表達方法更傳統(tǒng)一點
aMap.anotherkey = "i am map" <==為map添加新元素
Range類
??Range是Groovy對List的一種拓展,變量定義和大體的使用方法如下:
def aRange = 1..5 <==Range類型的變量 由begin值+兩個點+end值表示
左邊這個aRange包含1,2,3,4,5這5個值
如果不想包含最后一個元素,則
def aRangeWithoutEnd = 1..<5 <==包含1,2,3,4這4個元素
println aRange.from
println aRange.to
高級特性閉包
??閉包,英文叫Closure,是Groovy中非常重要的一個數(shù)據(jù)類型或者說一種概念了。閉包的歷史來源,種種好處我就不說了。我們直接看怎么使用它!閉包,是一種數(shù)據(jù)類型,它代表了一段可執(zhí)行的代碼。其外形如下:
def aClosure = {//閉包是一段代碼,所以需要用花括號括起來..
String param1, int param2 -> //這個箭頭很關鍵。箭頭前面是參數(shù)定義,箭頭后面是代碼
println"this is code" //這是代碼,最后一句是返回值,
//也可以使用return,和Groovy中普通函數(shù)一樣
}
簡而言之,Closure的定義格式是:
def xxx = {paramters -> code} //或者
def xxx = {無參數(shù),純code} 這種case不需要->符號
說實話,從C/C++語言的角度看,閉包和函數(shù)指針很像。閉包定義好后,要調(diào)用它的方法就是:閉包對象.call(參數(shù)) 或者更像函數(shù)指針調(diào)用的方法:閉包對象(參數(shù))比如
aClosure.call("this is string",100) 或者
aClosure("this is string", 100)
\\上面就是一個閉包的定義和使用。在閉包中,還需要注意一點:
\\如果閉包沒定義參數(shù)的話,則隱含有一個參數(shù),這個參數(shù)名字叫it,和this的作用類似。it代表閉包的參數(shù)。比如:
def greeting = { "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
\\等同于
def greeting = { it -> "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
\\但是,如果在閉包定義時,采用下面這種寫法,則表示閉包沒有參數(shù)!
def noParamClosure = { -> true }
\\這個時候,我們就不能給noParamClosure傳參數(shù)了!
noParamClosure ("test") \\<==報錯喔!
Closure使用中的注意點
??1、省略圓括號
??閉包在Groovy中大量使用,比如很多類都定義了一些函數(shù),這些函數(shù)最后一個參數(shù)都是一個閉包。比如:
public static <T> List<T> each(List<T> self, Closure closure)
上面這個函數(shù)表示針對List的每一個元素都會調(diào)用closure做一些處理。這里的closure,就有點回調(diào)函數(shù)的感覺。但是,在使用這個each函數(shù)的時候,我們傳遞一個怎樣的Closure進去呢?比如:
def iamList = [1,2,3,4,5] //定義一個List
iamList.each{ //調(diào)用它的each,這段代碼的格式看不懂了吧?each是個函數(shù),圓括號去哪了?
println it
}
上面代碼有兩個知識點:each函數(shù)調(diào)用的圓括號不見了!原來,Groovy中,當函數(shù)的最后一個參數(shù)是閉包的話,可以省略圓括號。比如
def testClosure(int a1,String b1, Closure closure){
//do something
closure() //調(diào)用閉包}那么調(diào)用的時候,就可以免括號!
testClosure (4, "test", {
println "i am in closure"
} )
//括號可以不寫..
注意,這個特點非常關鍵,因為以后在Gradle中經(jīng)常會出現(xiàn)圖1這樣的代碼:

??經(jīng)常碰見圖1這樣的沒有圓括號的代碼。省略圓括號雖然使得代碼簡潔,看起來更像腳本語言,但是它這經(jīng)常會讓我confuse(不知道其他人是否有同感),以doLast為例,完整的代碼應該按下面這種寫法:
doLast({
println 'Hello world!'
})
有了圓括號,你會知道 doLast只是把一個Closure對象傳了進去。很明顯,它不代表這段腳本解析到doLast的時候就會調(diào)用println 'Hello world!' 。但是把圓括號去掉后,就感覺好像println 'Hello world!'立即就會被調(diào)用一樣!
??2、如何確定Closure的參數(shù)
??另外一個比較讓人頭疼的地方是,Closure的參數(shù)該怎么搞?還是剛才的each函數(shù):
public static <T> List<T> each(List<T> self, Closure closure)
如何使用它呢?比如:
def iamList = [1,2,3,4,5] //定義一個List變量
iamList.each{ //調(diào)用它的each函數(shù),只要傳入一個Closure就可以了。
println it
}
看起來很輕松,其實:對于each所需要的Closure,它的參數(shù)是什么?有多少個參數(shù)?返回值是什么?我們能寫成下面這樣嗎?
iamList.each{String name,int x -> return x} //運行的時候肯定報錯!
??所以,Closure雖然很方便,但是它一定會和使用它的上下文有極強的關聯(lián)。要不,作為類似回調(diào)這樣的東西,我如何知道調(diào)用者傳遞什么參數(shù)給Closure呢?
??此問題如何破解?只能通過查詢API文檔才能了解上下文語義。比如下圖2:


??圖2中:each函數(shù)說明中,將給指定的closure傳遞Set中的每一個item。所以,closure的參數(shù)只有一個。findAll中,絕對抓瞎了。一個是沒說明往Closure里傳什么。另外沒說明Closure的返回值是什么.....。
??對Map的findAll而言,Closure可以有兩個參數(shù)。findAll會將Key和Value分別傳進去。并且,Closure返回true,表示該元素是自己想要的。返回false表示該元素不是自己要找的。示意代碼所示:
def result = aMap.findAll {
key, value ->
println "key=$key,value=$value"
if (key == "k1")
return true
return false
}
Closure的使用有點坑,很大程度上依賴于你對API的熟悉程度,所以最初階段,SDK查詢是少不了的。
??3、腳本類
??groovy也可以像java那樣寫package,然后寫類
package bean
class Person {
String name
String gender
Person(name, gender) {
this.name = name
this.gender = gender
}
def print() {
println name + " " + gender
}
}
import bean.Person
def name = 'EvilsoulM'
def person=new Person(name,"male");
person.print()
groovy和Java類很相似。當然,如果不聲明public/private等訪問權(quán)限的話,Groovy中類及其變量默認都是public的。
??4、腳本到底是什么
??Java中,我們最熟悉的是類。但是我們在Java的一個源碼文件中,不能不寫class(interface或者其他....),而Groovy可以像寫腳本一樣,把要做的事情都寫在xxx.groovy中,而且可以通過groovy xxx.groovy直接執(zhí)行這個腳本。這到底是怎么搞的?
??Groovy把它轉(zhuǎn)換成這樣的Java類:執(zhí)行 groovyc -d classes test.groovy groovyc是groovy的編譯命令,-d classes用于將編譯得到的class文件拷貝到classes文件夾下圖4是test.groovy腳本轉(zhuǎn)換得到的java class。用jd-gui反編譯它的代碼:

??test.groovy被轉(zhuǎn)換成了一個test類,它從script派生。
- 每一個腳本都會生成一個static main函數(shù)。
- 這樣,當我們groovy test.groovy的時候,其實就是用java去執(zhí)行這個main函數(shù)腳本中的所有代碼都會放到run函數(shù)中。比如,println 'Groovy world',這句代碼實際上是包含在run函數(shù)里的。
- 如果腳本中定義了函數(shù),則函數(shù)會被定義在test類中。
groovyc是一個比較好的命令,讀者要掌握它的用法。然后利用jd-gui來查看對應class的Java源碼。
??5、腳本中的變量和作用域
??前面說了,xxx.groovy只要不是和Java那樣的class,那么它就是一個腳本。而且腳本的代碼其實都會被放到run函數(shù)中去執(zhí)行。那么,在Groovy的腳本中,很重要的一點就是腳本中定義的變量和它的作用域。舉例:
def x = 1 // <==注意,這個x有def(或者指明類型,比如 int x = 1)
def printx(){
println x
}
//printx() <==報錯,說x找不到
為什么?繼續(xù)來看反編譯后的class文件。

??圖中,x也沒有被定義成test的成員函數(shù),而是在run的執(zhí)行過程中,將x作為一個屬性添加到test實例對象中了。然后在printx中,先獲取這個屬性。注意,Groovy的文檔說 x = 1這種定義將使得x變成test的成員變量,但從反編譯情況看,這是不對的.....(這是infoQ文章中說的,但是測試來說這句話是對的,應該是文章作者沒有定義成class)雖然printx可以訪問x變量了,但是假如有其他腳本卻無法訪問x變量。因為它不是test的成員變量。比如,我在測試目錄下創(chuàng)建一個新的名為test1.groovy。這個test1將訪問test.groovy中定義的printx函數(shù):
def atest=new test()
atest.printx()
這種方法使得我們可以將代碼分成模塊來編寫,比如將公共的功能放到test.groovy中,然后使用公共功能的代碼放到test1.groovy中。執(zhí)行groovy test1.groovy,報錯。說x找不到。這是因為x是在test的run函數(shù)動態(tài)加進去的。怎么辦?
import groovy.transform.Field; //必須要先import
@Field x = 1 // <==在x前面加上@Field標注,這樣,x就徹徹底底是test的成員變量了。
查看編譯后的test.class文件,得到:

??這個時候,test.groovy中的x就成了test類的成員函數(shù)了。如此,我們可以在script中定義那些需要輸出給外部腳本或類使用的變量了!
??eg:
??ScriptBase.groovy類 (用了filed 就相當這就是一個class 就不用再自己定義class了)
import groovy.transform.Field;
@Field author = 'EvilsouM'
@Field gender = 'male'
@Field age = 25//必須要先import
def printInfo() {
println "name->$author gender->$gender age->$age"
}
//或者自己定義class
class ScriptBase {
def author = 'EvilsouM'
def gender = 'male'
def age = 25//必須要先import
def printInfo() {
println "name->$author gender->$gender age->$age"
}
}
.groovy類
def Closure printAuthorInfo = {
String name, String gender, int age ->
println "name->$name gender->$gender age->$age"
}
def ScriptBase base = new ScriptBase()
base.printInfo()
printAuthorInfo.call(base.author, base.gender, base.age) //上面兩種方式都能拿到成員變量
文件I/O操作
??本節(jié)介紹下Groovy的文件I/O操作。直接來看例子吧,雖然比Java看起來簡單,但要理解起來其實比較難。尤其是當你要自己查SDK并編寫代碼的時候。整體說來,Groovy的I/O操作是在原有Java I/O操作上進行了更為簡單方便的封裝,并且使用Closure來簡化代碼編寫。主要封裝了如下一些了類:

- 讀文件Groovy中,文件讀操作簡單到令人發(fā)指:def targetFile = new File(文件名) <==File對象還是要創(chuàng)建的。然后打開http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html看看Groovy定義的API:
- 讀該文件中的每一行:eachLine的唯一參數(shù)是一個Closure。Closure的參數(shù)是文件每一行的內(nèi)容其內(nèi)部實現(xiàn)肯定是Groovy打開這個文件,然后讀取文件的一行,然后調(diào)用Closure...
def File targetFile = new File("build.gradle")
targetFile.eachLine {
String line ->
println line
}
- 直接得到文件內(nèi)容
targetFile.getBytes() <==文件內(nèi)容一次性讀出,返回類型為byte[]
- 使用InputStream.InputStream的SDK在 http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html
def ism = targetFile.newInputStream() //操作ism,最后記得關掉
ism.close
- 使用閉包操作inputStream,以后在Gradle里會??吹竭@種搞法
``
targetFile.withInputStream{
ism -> //操作ism. 不用close。Groovy會自動替你close
}
寫文件和讀文件差不多
??不再啰嗦。這里給個例子,告訴大家如何copy文件。
def srcFile = new File(源文件名)
def targetFile = new File(目標文件名)
targetFile.withOutputStream{
os->
srcFile.withInputStream {
ins->
os << ins //利用OutputStream的<<操作符重載,完成從inputstream到OutputStream //的輸出
}
}
以上的知識點都可以去官網(wǎng)查看API,Groovy的API文檔位于 http://www.groovy-lang.org/api.html
第一個Gradle項目,領略Gradle的風采

Gradle構(gòu)建腳步基本原理部分
構(gòu)建腳步介紹
??Gradle構(gòu)建中的兩個基本概念就是項目(project)和任務(task),每個構(gòu)建(build.gradle)至少包含一個項目,項目中包含一個或多個任務。在多項目構(gòu)建中,一個項目可以依賴于其他項目;類似的,任務可以形成一個依賴關系圖來確保他們的執(zhí)行順序。

腳步基本組成部分
項目(project)
??一個項目代表一個正在構(gòu)建的組件(比如一個jar文件),當構(gòu)建啟動后,Gradle會基于build.gradle實例化一個org.gradle.api.Project類,并且能夠通過project變量使其隱式可用。Project類的主要屬性和方法
??屬性group、name、version
??方法有apply、dependencies、repositories、task
??屬性的其他配置方式:ext、gradle.properties
??任務(task)
??任務對應org.gradle.api.Task。主要包括任務動作和任務依賴。任務動作定義了一個最小的工作單元??捎枚x依賴于其他任務、動作序列和執(zhí)行條件。
??**Task重要的方法
????dependsOn
????doFirst,doLast << **
?? task是個動作列,doFirst就是在動作列表最前面添加一個動作,doLast就是在動作列表的最后面添加一個動作

?? 自定義任務(Task)、Task的生命周期
?? 自定義創(chuàng)建文件夾任務


??構(gòu)建生命周期
??1、 初始化階段
??項目構(gòu)建開始的時候,會根據(jù)build.gradle構(gòu)建一個項目即project并且在這個腳本中隱式可用。在多項目構(gòu)建中這個階段也是很重要的,它會初始化所有需要參與到構(gòu)建中的項目。
??2、配置階段
??這個階段就是遍歷項目中所有task,生成task依賴順序以及執(zhí)行順序,根據(jù)配置代碼來生成的。配置代碼就是除了動作代碼外都是配置代碼,可以簡單的這么理解。 這個階段相當于初始化任務Task階段
??配置代碼如:

?? 3、執(zhí)行階段
??主要執(zhí)行動作代碼,執(zhí)行完后即一個構(gòu)建就完成了。
??動作代碼如:

??Gradle的工作流程其實蠻簡單,用一個圖15來表達:

??圖15告訴我們,Gradle工作包含三個階段:
??首先是初始化階段。對我們前面的multi-project build而言,就是執(zhí)行settings.gradle
??Initiliazation phase的下一個階段是Configration階段。
??Configration階段的目標是解析每個project中的build.gradle。比如multi-project build例子中,解析每個子目錄中的build.gradle。在這兩個階段之間,我們可以加一些定制化的Hook。這當然是通過API來添加的。Configuration階段完了后,整個build的project以及內(nèi)部的Task關系就確定了。恩?前面說過,一個Project包含很多Task,每個Task之間有依賴關系。Configuration會建立一個有向圖來描述Task之間的依賴關系。所以,我們可以添加一個HOOK,即當Task關系圖建立好后,執(zhí)行一些操作。
??最后一個階段就是執(zhí)行任務了。當然,任務執(zhí)行完后,我們還可以加Hook。
我在:
- settings.gradle加了一個輸出。
- 在posdevice的build.gradle加了圖15中的beforeProject函數(shù)。
- 在CPosSystemSdk加了taskGraph whenReady函數(shù)和buidFinished函數(shù)。
好了,Hook的代碼怎么寫,估計你很好奇,而且肯定會埋汰,怎么就還沒告訴我怎么寫Gradle。馬上了!
最后,關于Gradle的工作流程,你只要記?。?/p>
- Gradle有一個初始化流程,這個時候settings.gradle會執(zhí)行。
- 在配置階段,每個Project都會被解析,其內(nèi)部的任務也會被添加到一個有向圖里,用于解決執(zhí)行過程中的依賴關系。
- 然后才是執(zhí)行階段。你在gradle xxx中指定什么任務,gradle就會將這個xxx任務鏈上的所有任務全部按依賴順序執(zhí)行一遍!
??下面的這個鏈接對于學習Gradle很重要,
??https://docs.gradle.org/current/dsl/
依賴管理
幾乎所有的基于JVM軟件項目都需要依賴外部類庫來重用現(xiàn)有的功能。自動化依賴管理可以明確依賴的版本,可以解決因傳遞性依賴帶來的版本沖突。
工件坐標
group、name、version
** 倉庫**
mavenLocal/mavenCentral/jcenter,第一個本地倉庫,后面兩個是公共倉庫
自定義maven倉庫,就是maven私服倉庫,公司內(nèi)部為了代碼的安全肯定不會放到公共倉庫里面去,我們需要搭建一個內(nèi)部倉庫,管理自己的jar包。這個是實際中經(jīng)常用的。
文件倉庫,所謂的文件倉庫,就是本地機器上的文件路徑也可以作為倉庫,這個非常不建議大家使用,因為我們使用構(gòu)建工具就是為了讓構(gòu)建一致性,就是到處構(gòu)建,結(jié)果應該是一樣的。如果跟具體的機器有關的話,就違反了我們使用構(gòu)建工具的初衷。
依賴的傳遞性
B依賴A,如果C依賴B,那么C依賴A
正是因為有這種依賴的傳遞性,造成版本的沖突

??依賴階段配置
- compile、runtime
- testCompile、testRuntime
依賴階段關系

??編譯期依賴的,運行期必然依賴,運行期依賴的,編譯期未必依賴;源碼編譯依賴的,測試編譯必然依賴,測試編譯依賴的,源碼編譯期未必依賴;測試編譯依賴的,測試運行期必然依賴。


??mavenCentral公共倉庫網(wǎng)址http://search.maven.org/

??** 版本沖突解決方法**
?? 版本沖突實際列子

?? 解決方法步驟
?? 1、查看依賴報告
?? 2、排除傳遞性依賴
?? 3、強制制定一個版本
?? 基本不需要我們自己解決版本沖突,gradle會自動幫我們強制依賴最高版本的jar包
?? 修改默認解決策略方法,不然很難發(fā)現(xiàn)版本沖突
configurations.all{
resolutionsStrategy{
failOnVersionConflict()
}
}
排除傳遞性依賴的方法如下
compile('org.hibernate:hibernate-core:3.6.3.Final'){
exclude group:"org.slf4j",module:"slf4j-api"
//transitive=false
}
強制制定一個版本
configurations.all{
resolutionsStrategy{
force 'org.slf4j:slf4j-api:1.7.24'
}
}