學習筆記之構(gòu)建工具Gradle

導語:

隨著技術的發(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é)果:

img0.png

?? 容器類
?? 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這樣的代碼:


img1

??經(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:

img2

img3

??圖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反編譯它的代碼:

img4

??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文件。


img5

??圖中,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文件,得到:


img6

??這個時候,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來簡化代碼編寫。主要封裝了如下一些了類:

img7

  • 讀文件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的風采

img8

Gradle構(gòu)建腳步基本原理部分

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

img9.png

腳步基本組成部分

項目(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就是在動作列表的最后面添加一個動作

img10

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

img11

img12

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

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

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

img15

??圖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
    正是因為有這種依賴的傳遞性,造成版本的沖突

img16

??依賴階段配置

  • compile、runtime
  • testCompile、testRuntime

依賴階段關系

img17

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

img18

img19

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

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

img21

?? 解決方法步驟
?? 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'
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Groovy :是一種動態(tài)語言。 1:這種語言比較有特點,它和 Java 一樣,也運行于 Java 虛擬機中。簡單...
    PeytonWu閱讀 1,674評論 0 1
  • 這篇文章講給大家?guī)韌radle打包系列中的高級用法-自己動手編寫gradle插件。我們平常在做安卓開發(fā)時,都會在...
    呆萌狗和求疵喵閱讀 16,329評論 22 80
  • Gradle對于很多開發(fā)者來說有一種既熟悉又陌生的感覺,他是離我們那么近,以至于我每天做項目都需要他,但是他又是離...
    阿_希爸閱讀 9,692評論 10 199
  • Android Studio作為Android應用開發(fā)的官方IDE,默認使用Gradle作為構(gòu)建工具,所以對于An...
    feil0n9wan9閱讀 1,770評論 1 6
  • 本篇主要是個人學習gradle的筆記總結(jié) 一.開始之前 1. 為什么學習Gradle 采用DSL(Doma...
    zyq_neuq閱讀 1,612評論 2 12

友情鏈接更多精彩內(nèi)容