Gradle 從入門到精通

Gradle 從入門到精通

Java 構建Gradle 逐漸出如今各個開源軟件中,特別是Android平臺。雖然工具無對錯用的好便可,可是美帝亡我之心不死,從ANT(純手動)到MAVEN(套餐模式)再到Gradle(半自動步槍),每一次都是赤裸裸的學習成本。爲了跟上Google大爺的腳步,也只能委屈本身了。html

本文將從幾個方面介紹Gradle:java

  1. Groovy介紹
  2. Gradle介紹
  3. JavaWeb構建
  4. Android構建

Groovy

Groovy 是JVM上的一門語言,兼容Java,而且具備腳本語言的特性,至關於JAVA的擴展語言。這裏將從這幾方面介紹Groovy:mysql

  1. Groovy基礎
  2. 語法介紹

Groovy基礎

Groovy的安裝比較簡單,僅僅幾步就完成了:android

  1. 下載JDK,和GDK(Groovy SDK)。
  2. 配置JAVA_HOME,GROOVY_HOME,以及PATH指向bin中。
  3. 測試是否安裝Ok,能夠輸入 groovy -e "println 'hello groovy'" 測試

Groovy安裝

一般在使用Groovy的時候,一份API文檔是至關重要的GroovyAPIgit

好比說Groovy的全部對象都集成GroovyObject,經過查看API文檔,就能夠知道幾個很是重要的方法:github

GroovyObject

這些方法都是比較重要的,涉及到動態語言的特性。如spring

getProperty(String s) -> Groovy動態屬性讀取API setProperty(String s,Object o) -> Groovy動態屬性設置APIsql

經過這兩個API,就能夠在運行時動態添加/讀取屬性,而且也增強了閉包做用域的動態性。shell

語法介紹

Groovy 是一門兼容Java的語言,而且在Java的語言基礎上添加了一些動態語言的特性。**Groovy能夠當作是Java的擴展集合。**接下來,將會介紹一些Groovy特有的語法特性,這些特性在Gradle中大範圍的被使用,瞭解這些語法,利於對Gradle的學習。編程

變量

Groovy 支持類型推斷,也就是定義變量的時候,不須要指定類型,統一使用def便可,如:

def name = "DARKGEM";

固然也能夠採用:

String name = "Hello World";

來定義變量了。

分號

Groovy支持語句最後面不用添加分號(;)

def w1 = "Hello World";
def w2 = "Hello World"

這兩句語句是等價的。

函數

參數類型無需聲明

在Groovy中,函數的參數也能夠不定義它的類型,而是採用直接聲明變量名便可,如:

void say1(content){
    println content;
}

void say2(String content){
    println content;
}

上面兩個函數是等價的。

返回類型無需指定

Groovy的函數,其實也不須要指定函數返回類型,經過def定義函數便可,如:

def add(a,b){
    return a+b;
}

int add(a,b){
    return a + b
}

它們是等價的

return語句

Groovy 的函數,其實也不須要return關鍵字,它會將最後一句的運行結果表達式,做爲return的結果,如:

def add(a,b){
    a + b
}

def add(a,b){
    return a + b
}

它們是等價的。

函數調用

Groovy中對函數的調用,有時候(至少存在一個參數)能夠省略括號,這個特性使得Gradle腳本看起來更像是DSL了。如:

println "NO () CALL"
println("NO () CALL")

它們是等價的。

可是有時候,是不能省略括號的

def func(){
    println 'i am function'
}

func()//success
func//error

由於func函數沒有參數,因此調用的時候,若是省略了括號,**則func會被認爲是屬性。**因此只能採用func()調用方式。

註釋

Groovy的註釋和Java同樣,支持 // 和 /**/ 的寫法,如:

// 這是單行注視
/**
    這
    是
    多
    行
    注
    釋
*/

字符串

Groovy 支持Java字符串,也支持動態推斷[$表達式],如:

def name = 'DARKGEM'

println 'I am $name'
println "I am $name"
println "I am ${name}"
println "1 + 2 = ${1+2}"
println '''
	SELECT
		*
	FROM
		T_USER
'''

輸出結果爲:

Groovy字符串運算

可空語法(optional)

在Java中,最討厭的就是空指針的處理了,而groovy提供了比較優雅的判斷空值的語句(?),如:

option?.call() -> 其中option多是空指針對象

經過這種語句,就能夠當option==null的時候,不進行調用call方法,避免拋出NullPointException。

集合

在Groovy中,優化了對集合的操做,避免了一些比較醜陋的語法出現。

List

經過Groovy的語法特性,能夠很是簡單的對List進行初始化和操做

def list = [1,2,3,"4"] //初始化爲ArrayList對象,而非 Object[] 對象類型
println list[3]  == '4' // 直接讀取數組元素
println list.join(",") == '1,2,3,4' // 支持join操做
println list.count(2) == 1 // 統計List中數值爲2的元素個數
list[99] = '100' //直接賦值到第100的位置
println list.size ==  100 // 此時List對象的元素爲100個
Map

Groovy對Map類型,提供了很是簡單易用的語法特性:

def map = [:] //聲明瞭map對象
def map = [key1 : 'val1' , 'key2' : 'val2'] //初始化map對象
println map.key1 == 'val1' // 讀取key1元素
println map["key1"] == 'val1' //讀取key1元素,注意[]裏面須要爲字符串
map.key1 = 'change val1' // 修改key1的元素
map.key3 = 'val3' //添加新元素

若是一個函數的參數爲Map類型,那麼能夠採用以下的方式調用:

def func(Map m){
    println m
}

func key1:'val1', key2:'val2' //免去了() 以及[]

func([key1:'val1',key2:'val2']) //完整的調用格式
Range

在Groovy中,支持Range類型,好比說

1..3 // 1-3 
1..<5 // 1-4

這種寫法,一般和迭代合起來用。

閉包

Groovy中最具備表明性的語法特性就是閉包(closure),閉包在Groovy中是一等公民。經過閉包,咱們能夠省略不少代碼。咱們看一下閉包最基本的寫法:

def c = {
    println "it's closure"
}
c(); // it's closure

僅僅經過**{}**就編寫出一個閉包,很是的方便。而閉包的語法結構爲:

{
    [param][,param]->code
}

的格式。其中,默認參數的關鍵字爲it。如:

def c = {
    println it
}
c('hello') // hello

返回的結果和函數同樣能夠經過return指定,也能夠默認採用最後一句表達式的運行結果。如:

def c ={
    1 +2
}
def c = {
    return 1+2
}

是等價的。

而閉包的調用,一般有三種方法:

  1. closure(args); //調用了閉包對象的call方法
  2. closure.call(args); //調用閉包的call方法
  3. closure 'callMe' //省略了括號,而且調用了閉包的call方法

而若是函數的參數爲閉包的話,就造成了以下的語法格式:

def func(Closure c){
    c()
}

func {
    println 'run closure'
}

是否是和Gradle的腳本很是的相似。

迭代

Groovy對迭代,提供了很是NB的支持,如:

println '-------------list-----------'
def list = [1,2,3,4]
list.each {
    println it
}
println '-------------map--------------'
def map = [k1:'v1',k2:'v2']
map.each{k,v->
    println "$k:$v"
}
println '-----------number range------------'
for(def i in 1..4){
    println i
}
println '-----------char range------------'
for(def i in 'a'..'d'){
    println i
}

運行結果:

Groovy迭代

Groovy類

Groovy是支持類的,而且還具備本身的特性:

  1. 全部的屬性/方法,默認爲public訪問屬性
  2. 全部的屬性/方法,均可以經過def定義
  3. 默認添加get/set方法,以及默認的構造函數

見一個類的聲明:

class DGM{
	def a
	def b
	//add
	def add(){
		println "a + b = ${a + b}"
	}
}

DGM rhs = new DGM(a:1,b:2) //初始化,注意不能省略括號
rhs.add(); //調用方法
println rhs.a //訪問a屬性,是經過內置函數getA()實現
println rhs.getA() //訪問內置函數getA

運行結果爲:

Groovy類

經過javap查看groovyc編譯成字節碼後的DGM.class文件,能夠看到默認的方法:

groovy類字節碼

對象調用

對象也能夠像函數同樣被調用 a();

class A{
	public Object call(Object... args){
		println 'callMe'
	}
}

A a = new A()

a()

運行結果:

對象調用

操做符重載

Groovy支持一個重要的特性就是操做符重載了,經過這個特性,能夠很是方便的完成一些經常使用的操做,如List讀取某個元素的數值等。下面介紹leftShift(<<)的重載

def list = [1,2,3,4]
list << 5 //添加5到list中
list << 6 //添加6到list中
println list

運行結果爲:

List leftShift

能夠發現,在gradle中常常出現task <<寫法,其實就是重載了leftShift函數。

動態屬性和delegate機制

Groovy是一門動態語言,它支持動態屬性。

class A{
	def prop = [:]

	void setProperty(String propertyName, Object newValue){
		prop[propertyName] = newValue
	}

	Object	getProperty(String propertyName){
		return prop[propertyName]
	}
}

A a = new A()
a.a = 1;
println a.a

運行截圖:

Groovy 動態屬性

能夠發現:經過重載GroovyObject#setProperty/getProperty就實現了動態屬性的特性。這個特性在Gradle常常被使用。

這個特性,結合閉包的delegate機制,就能夠改變閉包的做用域。如:

class A{
	def prop = [:]

	void setProperty(String propertyName, Object newValue){
		prop[propertyName] = newValue
	}

	Object	getProperty(String propertyName){
		return prop[propertyName]
	}
	def func (Closure c){
		c()
	}
}

def closure = {
	//運行函數
	func {
		println 'run closure'
	}
	//經過動態屬性,設置新的屬性
	newProperty = 'newValue'

}
//指向A對象
closure.delegate = new A()
//做用域優先採用delegate
closure.setResolveStrategy Closure.DELEGATE_FIRST
//運行
closure()
//讀取閉包中設置的屬性
println closure.delegate.newProperty

運行結果:

Groovy delegate和動態屬性

經過這種方法,Gradle就能夠動態的加載插件/任務/配置屬性,而且能夠直接調用方法/屬性。

調用語法

針對調用的語法:

func('arg1','arg2')

也能夠省略括號(爲函數的時候,參數須要多餘一個)

func '1','2'

若函數的時候沒有參數,則須要添加括號

func()

若是編寫以下格式的語法,則認爲是查詢屬性

func //查詢屬性,而非調用

這點和shell語法有極大的不一樣。

groovy的處理調用方式以下:

  1. 首先會提取func名稱,查詢當前做用域是否存在該名稱的屬性
  2. 若是存在屬性,則會調用該屬性的call方法**(閉包,自定義類的call方法)**
  3. 若是不存在屬性,則會調用invokeMethod方法,查詢到合適的方法,而後進行調用。
  4. 若是都不存在,則拋出異常

Groovy總結

三言兩語,仍是比較難全面的介紹Groovy語言的,可是經過上面這些語法特性,基本上可讓初學者能讀通Gradle腳本了。若是要深刻的使用Gradle,那麼增強Groovy語言的學習也是有必要的。這裏給幾點意見:

  1. 編寫完Groovy腳本後,能夠經過groovyc來編譯,獲取class文件,而後反編譯(jd-gui/javap)查看groovy是如何轉換爲java語言的,加深的groovy語言的理解。

  2. api手冊不離手,多看看javadoc。

Gradle

簡介

Gradle是一個基於Apache Ant和Apache Maven概念的項目自動化建構工具。它使用一種基於Groovy的特定領域語言(DSL)來聲明項目設置,拋棄了基於XML的各類繁瑣配置。

面向Java應用爲主。當前其支持的語言限於Java、Groovy和Scala,計劃將來將支持更多的語言。

gradle對多工程的構建支持很出色,工程依賴是gradle的第一公民

BY 百度百科

安裝和命令

安裝

Gradle的安裝很是的簡單,只須要以下幾步既能夠完成:

  1. 下載JDK,配置好JAVA_HOME,以及添加bin到PATH中
  2. 下載Gradle,配置好GRADLE_HOME,以及添加bin到PATH中
  3. 測試Gradle是否安裝成功,在CMD下輸入 gradle -v 進行測試

運行截圖:

Gradle安裝

經常使用指令

  1. gradle --help 查看支持的指令
  2. gradle tasks [--all]查看可執行的任務
  3. gradle projects 查看全部的項目
  4. gradle dependencies 查看依賴
  5. gradle ProjectA:TaskA 多項目,注意使用冒號:

**注意Gradle支持任務簡寫:**好比說存在兩個任務 assembleDebug 和 assembleRelease, 咱們不須要所有輸入,只須要輸入 gradle assD便可運行 assembleDebug 任務。

基本概念

Gradle 是一門基於Groovy語言的構建框架。gradle支持ant,maven構建方式,而且和ide能比較完美的整合。

Project Task概念

一個 Gradle 構建項目是由若干個project組成,而一個project是由若干個task組成。

Project概念針對某一個構建項目。一個Project包含了若干Task, 依賴關係,配置屬性等。一般Project通過一系列過程後會產出構建成品,如jar,war等。注意:Gradle容許項目依賴

**而Task是屬於Project的,經過一系列的Task組合,咱們能夠構建一個項目。**好比說:Project要輸出一個jar,那麼task通常有編譯,打包等。經過組合task,完成了最終產出jar的構建。

任務之間經過dependsOn來決定構建依賴。好比說debug依賴classes任務,則調用gradle debug的時候,會優先執行classes任務。以下圖:

Gradle 任務依賴

一般咱們說的構建,就是經過指定某一個項目下(多項目)的某個task(build)來執行一系列的構建task,最終獲取想要的結果,如jar,war。

項目結構

一個gradle項目的基本結構爲:

單項目結構

Gradle單項目結構

多項目結構

Gradle多項目結構

其中,setting.gradle 爲: include 'ProjectA','ProjectB' 來指示包含的項目

build.gradle 和 settings.gradle

build.gradle 文件是描述項目是如何構建的關鍵。一般這個文件描述了項目引用的插件(插件包含了某種特定類型的構建任務集合,如java,android),任務配置(源碼位置,依賴關係),第三方倉庫等信息。

settings.gradle 文件一般是描述項目中的一些基本配置,如通用的task,在多項目構建中settings.gradle 還須要描述被包含的項目。

構建流程

gradle 的構建流程,通常會按照以下的過程:

  1. 初始化階段:初始化gralde運行環境。
  2. 配置階段:依次讀取build.gradle等文件,而後運行它們。**注意:buildscript{}等會優先執行,即便在build.gradle最後面編寫。**tips:經過Gradle的自定義語法解析器實現。
  3. 計劃執行階段:此時已經獲取了全部項目的依賴關係和task依賴關係,就能夠生成執行路徑圖。
  4. 執行階段:根據執行路徑圖,執行不一樣項目的task直到最後完成構建過程

在各個階段,咱們能夠經過Gradle提供的Hook API進行截取。如:gradle.taskGraph.whenReady

Hello World

如今,咱們來實際編寫一些gradle腳本,加深理解。

建立腳本

首先,咱們創建一個文件夾命名爲gradle 而後,在目錄下建立一個build.gradle的文件 最後,在build.gradle中輸入:

//定義hello這個任務
task hello << {
	println 'Hello'
}
    /**
        能夠發現,上面的task,利用了操做符重載,其重載的API仍是doLast。
        task hello {
            doLast{
                println 'Hello'
            }
        }
    */
//定義world任務,而且依賴hello任務
task world(dependsOn:hello) <<{
	println 'World'
}

執行腳本

咱們能夠經過gradle tasks 查看全部被支持的任務,發現咱們剛剛添加的任務已經出如今任務列表中了。 咱們在gradle/目錄下,執行** gradle world **命令,運行結果爲:

gradle hello world

動態任務

gradle 支持動態任務,也就是說,咱們能夠在腳本運行的時候,定義任務:

5.times {
	task "task$it" <<{
		println "$it"
	}
}

運行命令 gradle tasks 查看全部的任務:

動態任務

task自定義屬性

咱們能夠在task中,添加一些咱們自定義的屬性

task hello {
    ext {
        mark = 'darkgem'
    }
}
println hello.mark

運行結果: task自定義屬性

這個實現的原理就是Groovy的動態屬性

默認任務

咱們能夠指定gradle的默認任務,如:

defaultTasks 'run'

task compile <<{
	println 'compile...'
}

task run(dependsOn:compile) <<{
	println 'run....'
}

這樣子,咱們直接輸入 gradle 就直接執行 run task:

Gradle 默認任務

Hook API 使用

Gradle 在不一樣階段準備完成後,會執行Hook API,讓用戶能夠有機會修改某些流程或者配置等,好比說:

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'
    }
}

運行結果:

Gradle Hook API

能夠發現不一樣的任務,就出現了不一樣的執行結果。

多項目

如今,來介紹一下多項目構建。目錄結構爲:

Gradle多項目文件結構

settings.gradle:

include 'ProjectA','ProjectB'

ProjectA#build.gradle

task taskA <<{
	println 'taskA'
}

ProjectB#build.gradle

task taskB <<{
	println 'taskB'
}

執行gradle tasks -all 結果爲:

多項目任務列表

task語法分析

咱們能夠發現,gradle 的task語法和以前咱們介紹的groovy語法有些區別,如:

task first<<{
println 'first'
}
task hello(dependsOn:'first') {
    println 'hello'
}

這種寫法,使用groovy語法的標準,沒法分析出task究竟是什麼東西。其實呢,gradle爲了腳本寫的好看,作了一些特別的工做。 上述的語句,在gradle分析腳本後,會轉換爲:

task('first',{
    doLast{
        println 'hello'
    }
})
task('hello',{
    dependsOn 'first'
println 'hello'
});

這種格式。這樣子,一切就真相大白了。**說白了就是gradle實現了本身的語法糖。**而經過後面的閉包,配置了這個task的屬性,如依賴,後置處理等。這個原理涉及到Groovy的delegate機制。

依賴管理

repositories{}

經過 repositories{} 能夠配置maven,ivy,local倉庫。這樣子,在dependencies{}聲明的依賴就能夠經過repositories{}中指定的倉庫查詢到具體的JAR資源。

configurations{}

configurations{} 記錄着項目中各個分組(compile,runtime)的依賴信息。**這些依賴信息來自於:引用的插件,dependencies{第三方庫},sourceSets{源碼},自定義配置,任務的構建產品(JAR)等。**以下是一個簡單的自定義分組信息:

configurations.create('myCompile')
dependencies{
    myCompile fileTree('lib')
}

這樣子,咱們就聲明瞭一個名字爲myCompile的依賴分組。

configurations{}主要的做用在於:

  1. 項目編譯的時候,須要依賴這個分組來獲取指定的資源(JAR,XML,Java等)。
  2. 多項目編譯的時候,dependencies#configurationName project(path: ':projectA', configuration: 'someOtherConfiguration'),默認configuration爲default。

注意:如compile,runtime 之類的configuration已經默認的被添加到java插件中,因此通常狀況下不要使用這幾種configuration。

dependencies{}

在gradle中dependencies{}是一等公民,它描述了configurations{}中分組依賴的第三方資源。咱們能夠把依賴簡單的分紅兩大類:

  1. gradle依賴:主要是gradle運行的時候,須要加載一些插件,如android等,此時須要配置它。

  2. 項目編譯/運行依賴:編譯一個項目,一般須要依賴其餘項目或者JAR。

gradle依賴

gradle依賴指定,一般在buildscript{}模塊中聲明,若是是一個多項目構建,則只須要在root目錄下的build.gradle中添加gradle依賴便可。好比說,android gradle依賴支持:

buildscript {
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath 'com.android.tools.build:gradle:1.5.0'
	}
}

這樣子就配置好了gradle的運行依賴,在使用的時候,調用 apply plugin: 'android' 既能夠引入android插件。

項目依賴

在編譯項目的時候,也常常出現依賴外部JAR,項目等狀況。此時,咱們只須要配置 dependencies 便可引入JAR。

dependencies {
	//for dependencies found in artifact repositories you can use
	//the group:name:version notation
	compile 'commons-lang:commons-lang:2.6'
	testCompile 'org.mockito:mockito:1.9.0-rc1'

	//map-style notation:
	compile group: 'com.google.code.guice', name: 'guice', version: '1.0'

	//declaring arbitrary files as dependencies
	compile files('hibernate.jar', 'libs/spring.jar')

	//putting all jars from 'libs' onto compile classpath
	compile fileTree('libs')
	// dependencies project
	compile project(':someProject')
	//dependencies project and select configuration
	configurationName project(path: ':projectA', configuration: 'someOtherConfiguration')

}

一個Java項目,一般依賴的關係如上圖描述。值得注意的是,依賴倉庫的JAR寫法:

dependencies jar

這種寫法,和MAVEN一致。

gradle腳本編寫

在編寫gradle腳本的時候,網上的配置有時候不能解決你手頭的問題,因此咱們須要求助於Gradle文檔。

首先,咱們須要理解,一個gradle腳本通過處理後其實最後就是一個Groovy的腳本,區別點爲:

  1. gradle腳本中task具備語法糖。
  2. gradle腳本經過groovy的delegate和動態屬性,實現了做用域爲一條對象鏈,該對象鏈包括了Project,Java Plugin等。

理解了這兩點,咱們就基本上能夠抱着API編寫腳本了。下面將介紹gradle腳本如何和Gradle文檔對應起來。

build.gradle

    //此時做用域鏈爲Project
    //調用 Project#apply(Map map) 方法
	apply plugin: 'java' 
    /**
        語法糖 task('hello',{ 
            //此時,做用域鏈爲Task
            doLast { // 調用Task#doLast方法
                println 'hello'
            }
        })

    */
    task hello { 
        doLast{
            println 'hello'
        }
}

翻閱Gradle的文檔(DSL,Javadoc),咱們能夠查詢到其餘有用的API來支持咱們的gradle編程。

gradle總結

本小節大體的描述了一下gradle這個構建工具,而且和groovy語言作了一下對比。能夠發現,gradle腳本就是groovy的腳本。

JavaWeb構建

一個JavaWeb的項目構建和一個普通Java項目的構建最大的區別在於:JavaWeb有固定的目錄結構。如圖:

JavaEE目錄結構

因此,咱們只要把項目構建的結果class存放到classes/目錄下,就是一個標準的JavaEE項目了。

接下來,咱們來看看Gradle提供的Java plugin的任務流程:

JavaPlugin Task流程

能夠發現,咱們其實僅僅須要** classes task **便可完成對編譯完成的class輸出。

構建項目目錄,該項目的目錄相似eclipse提供的JavaEE目錄結構:

eclipse JavaEE項目目錄

而構建的腳本以下:

//使用Java插件
apply plugin: 'java'

//配置編譯選項
compileJava {
            options.encoding = 'UTF-8'
            sourceCompatibility = "1.6"
            targetCompatibility = "1.6"
}

//配置源碼信息
sourceSets {
	main {
                    //指定main的源碼輸出位置
		output.resourcesDir = output.classesDir = 'WebContent/WEB-INF/classes/'
                    //源碼目錄
		java.srcDir('src')
                    //資源文件目錄
		resources.srcDir('src')
	}
}
//配置依賴
dependencies {
            //LIB依賴
	compile fileTree('WebContent/WEB-INF/lib/')
            //servlet api依賴
	compile fileTree('jetty/')
}

//動態生成任務
({
	//屬性配置和
	def props = [
			JDBC_CLASS_NAME  : ['com.mysql.jdbc.Driver', 'com.mysql.jdbc.Driver'],
			JDBC_URL         : ['jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=UTF-8', 'jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=UTF-8'],
			JDBC_USR         : ['root', 'root'],
			JDBC_PASSWD      : ['root', 'root'],
			JDBC_MAX_ACTIVE  : [10, 10],
			JDBC_INITIAL_SIZE: [1, 1],
			JDBC_MIN_IDLE    : [5, 5]
	]
	//動態生成任務的名稱,注意 props裏面的屬性和這個序列對應
	['debug', 'release'].eachWithIndex { val, idx ->
		task "$val"(dependsOn: 'classes') << {
			//將配置信息,寫入指定文件中
			Properties properties = new Properties()
			File file = new File(sourceSets.main.output.resourcesDir, 'project.properties')
			OutputStream fos = new FileOutputStream(file)
			props.each { key, value ->
				properties.setProperty(key, String.valueOf(value[idx]))
			}
			properties.store(fos, String.format("mode=%s", val));
		}
	}
})();

經過這個腳本,咱們能夠構建出一個JAVAEE的項目,而且該腳本支持生產環境和測試環境不一樣配置的特色。而腳本中的API調用都來自於Gradle Java DSL Plugin的描述。

項目地址:javaee-framework

Android構建

在 Google I/O 2013 大會上,發佈了一款新的Android工具--Android Studio 以及 Gradle 後,eclipse 和 ant 逐漸被google 拋棄了。如今android小組的構建工具都採用gradle,而且它對應的插件目前版本已經2.0了。

構建Hello World

經過gradle構建android項目是很是簡單的。首先建立項目:

android create project -n Demo -a MainActivity -k org.darkgem -v 1.5.0 -t android-15 -g -p .

這樣子就建立了一個簡單的android gradle 項目。

android gradle 目錄

可是由於gradle的版本兼容性的問題,因此咱們還須要修改一下build.gradle腳本:

//gradle初始化的時候,須要加載的資源指定
buildscript {
	repositories {
		mavenCentral()
	}
            //加載android gradle 插件JAR
	dependencies {
		classpath 'com.android.tools.build:gradle:1.5.0'
	}
}
    //加載android 插件
apply plugin: 'android'

android {
            //配置android sdk版本
	compileSdkVersion 'android-15'
            //配置編譯工具版本
	buildToolsVersion '23.0.2'
}

而後咱們運行指令gradle build,進行構建。結果發現異常:

android lint

能夠發現,在運行task lint的時候,發生了異常,查看lint 報告:

lint report

能夠發現,實際上是由於local.properties文件中關於sdk.dir寫法出現問題,咱們修改一下就能夠了:

sdk.dir=D:\\Link\\android-sdk -> sdk.dir=D\:\\Link\\android-sdk

再次運行gradle build就能夠執行成功了。其輸出的apk在:

gradle android apk

關於lint

android lint是一款靜態代碼檢測工具,經過lint能夠在編譯期就能夠發現許多問題,如調用太新的API等。這個檢測是至關有必要的。因此不建議在腳本中添加:

android {
	lintOptions {
		abortOnError false
	}
}

這會致使lint檢測無效。

值得注意的是:在Android Studio中,若是點擊Debug按鈕進行調試,是不會運行task lint的,因此須要用戶手動的調用 task lint,使得在開發的時候,就知道問題出如今哪裏。

sourceSets

gradle不像maven同樣,源碼和目錄這些都是固定位置的,咱們能夠經過配置,修改默認位置:

sourceSets{
	main.setRoot('app')
}

這樣子就把mian目錄,從默認位置的 src/main/ 提取到了 app下面。還能夠配置其餘的一些文件和目錄:

sourceSets {
	main {
                    //mainifest文件
		manifest.srcFile 'AndroidManifest.xml'
                    //源碼目錄
		java.srcDirs = ['src']
                    //源碼資源目錄
		resources.srcDirs = ['src']
                    //aidl目錄
		aidl.srcDirs = ['src']
                    //layout等資源目錄
		res.srcDirs = ['res']
                    //assets目錄
		assets.srcDirs = ['assets']
                    //SO文件目錄
                    jniLibs.srcDirs = ['libs']
	}
}

這樣子,就講該android項目的源碼目錄結構變成和eclipse同樣了。

依賴管理

android gradle的依賴管理和java同樣,只須要配置好 repositories{} 和 dependencies{} 便可。如:

//配置倉庫
repositories {
	mavenCentral()
}

//配置依賴
dependencies {
	//按照maven名稱加載jar
	compile 'com.google.guava:guava:11.0.2'
	//加載目錄下全部的jar
	compile fileTree(dir: 'libs', include: ['*.jar'])  //多個文件
}

簽名

android的構建必不可少的就是簽名了,經過簽名。通常簽名分爲兩種:DEBUG 和 RELEASE ,因此通常只須要兩種類型的簽名便可:

android{
	signingConfigs {
		debug {
			storeFile file("debug.keystore")
		}
		release {
			storeFile file("release.keystore")
			storePassword "darkgem"
			keyAlias "release"
			keyPassword "darkgem"
		}
	}
        buildTypes {
                //release類型配置
	    release { 
                    //指定使用哪一個簽名配置
		signingConfig signingConfigs.release
	    }
        }
}

值得注意的是DEBUG簽名是固定格式的:

  1. key-alias 必須爲androiddebugkey
  2. keystore 與key alias的密碼必須爲android

一般,咱們只須要拷貝~/.android/debug.keystore 便可。

代碼混淆

爲了保護咱們的代碼,一般在發佈版本的時候進行代碼混淆處理:

buildTypes {
	release {
		minifyEnabled true
		proguardFile 'proguard-project.txt'
	}
}

這樣子就在release的時候進行代碼混淆了。

值得注意的是:

  1. 編程中,不要使用JSON<->Object的變化,這很是容易致使混淆後,屬性沒法使用。
  2. 混淆完成後,會出現mapping文件,咱們須要保留它,在追蹤異常的時候,經過它來尋找到真實代碼錯誤點。

完整腳本

下面給出一個比較完成的腳本,充分的考慮的測試環境和發佈環境配置等:

//gradle初始化腳本
buildscript {
	//第三方倉庫
	repositories {
		mavenCentral()
	}
	//android gradle plugin 插件依賴
	dependencies {
		classpath 'com.android.tools.build:gradle:1.5.0'
	}
}
//使用android 插件
apply plugin: 'android'

android {
	//sdk 版本
	compileSdkVersion 'android-15'
	//構建工具版本
	buildToolsVersion '23.0.2'
	//源碼配置
	sourceSets{
		main.setRoot('app')
	}
	//簽名配置
	signingConfigs {
		debug {
			storeFile file("debug.keystore")
		}
		release {
			storeFile file("release.keystore")
			storePassword "darkgem"
			keyAlias "release"
			keyPassword "darkgem"
		}
	}
	//構建類型
	buildTypes {
		//測試版本
		debug{
			//測試版本對AndroidMainfest.xml 中關鍵字替換
			//UMENG
			manifestPlaceholders = [UMENG_APPKEY: "56947a48e0f55a0635002c3a", UMENG_CHANNEL : "DARKGEM"]
			//在 BuildConfig中,添加自定義的屬性
			//BUILD CONFIG
			buildConfigField "String", "MAIN_API_URL", "\"http://darkgem.org/api/v1/\""
		}
		//發佈版本
		release {
			//開啓混淆處理
			minifyEnabled true
			//簽名引用
			signingConfig signingConfigs.release
			//混淆文件
			proguardFile 'proguard-project.txt'
			//發佈版本對AndroidMainfest.xml 中關鍵字替換
			//UMENG
			manifestPlaceholders = [UMENG_APPKEY: "56947a48e0f55a0635002c3a", UMENG_CHANNEL : "DARKGEM"]
			//在 BuildConfig中,添加自定義的屬性
			//BUILD CONFIG
			buildConfigField "String", "MAIN_API_URL", "\"http://darkgem.org/api/v1/\""
		}
	}
	//javac 配置
	compileOptions {
		encoding 'UTF-8'
		sourceCompatibility JavaVersion.VERSION_1_6
		targetCompatibility JavaVersion.VERSION_1_6
	}
}
//依賴
dependencies {
	compile fileTree(include: ['*.jar'], dir: 'app/libs/')
}

項目地址:android-framework

android多項目構建

現今,許多android開源項目都開始使用gradle進行構建了。特別是support-v7包中的View類。若是直接進行拷貝引入,你會發現很是的困難。此時,咱們須要藉助gradle多項目編譯了。其方法和以前介紹的gradle多項目同樣。

值得注意的點有:

  1. 由於最新的support-v7包是用最新的sdk編譯的,因此,若是你使用舊版本的sdk編譯項目的話,可能出現資源文件沒法解析之類錯誤。此時,你須要把sdk提供到最新便可。

  2. 一般,咱們只須要在root/build.gradle中引入android gradle插件依賴,不須要每個build.gradle都引入一次android gradle plugin的依賴。

  3. 多項目中,引入插件的時候,須要區分application和library。

總結

這篇文章斷斷續續的寫了三天,算是把gradle基礎概念學習了一遍。獲得最重要的經驗就是:多看文檔。

參考

GroovyAPI

精通 Groovy

groovy–運算符重載

gradle javadoc

gradle dsl

gradle java plugin

Gradle 使用指南

深刻理解Android之Gradle

使用Gradle構建Android項目

android gradle dsl

Gradle在大型Java項目上的應用

相關文章
相關標籤/搜索