Gradle學(xué)習(xí)

Android默認(rèn)使用Gradle作為構(gòu)建工具斑鼻。

Why Gradle

Android Studio Project Site的描述如下

gradle_why.png

Gradle的出現(xiàn)滿足了很多現(xiàn)在構(gòu)建工具的需求,Gradle提供了一個(gè)DSL(領(lǐng)域特定語(yǔ)言),一個(gè)約定優(yōu)于配置的方法娱俺,還有更強(qiáng)大的依賴管理,Gradle使得我們可以拋棄XML的繁瑣配置废麻,引入動(dòng)態(tài)語(yǔ)言Groovy來定義你的構(gòu)建邏輯荠卷。

Groovy

由于Gradle基于Groovy,要理解Gradle烛愧,必須先了解Groovy油宜。
Groovy 被很多人認(rèn)為是一種腳本語(yǔ)言掂碱,其實(shí)不準(zhǔn)確,官網(wǎng)表述:

groovy_0.png

Groovy是一種基于JVM(Java虛擬機(jī))的動(dòng)態(tài)語(yǔ)言慎冤,它結(jié)合了Python疼燥、Ruby和Smalltalk的許多強(qiáng)大的特性,Groovy 代碼能夠與 Java 代碼很好地結(jié)合蚁堤,也能用于擴(kuò)展現(xiàn)有代碼醉者。由于其運(yùn)行在 JVM 上的特性,Groovy 可以使用其他 Java 語(yǔ)言編寫的庫(kù)披诗。

基礎(chǔ):
  • 注釋和java一樣
  • 語(yǔ)句可以不以分號(hào)結(jié)尾
  • 定義變量是可以不指定類型撬即,定義變量使用def關(guān)鍵字(不強(qiáng)制,但推薦呈队,def會(huì)改變作用域)

按套路剥槐,現(xiàn)在該上“HelloWorld”了

java版HelloWorld:

public class Test {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

Groovy版HelloWorld:

println "Hello World"

Groovy版本比java版本簡(jiǎn)單很多,我們可以將groovy文件編譯為class查看:

groovyc -d classes test.groovy

編譯后得到test.class:


groovy_test1.png
  • test.groovy被轉(zhuǎn)為一個(gè)繼承自Script的test類
  • 每一個(gè)腳本都會(huì)生成一個(gè)main函數(shù)
  • 腳本中的代碼都會(huì)放到run函數(shù)中
  • 如果腳本中定義了函數(shù)宪摧,則函數(shù)會(huì)被定義在test類中

之前說def會(huì)改變變量作用域粒竖,舉個(gè)栗子:

def num = 1 // 也可以 int num = 1
def printNum() {
    println num
}
printNum() 

結(jié)果是運(yùn)行異常:

Caught: groovy.lang.MissingPropertyException: No such property: num for class: test
groovy.lang.MissingPropertyException: No such property: num for class: test
    at test.printNum(test.groovy:4)
    at test.run(test.groovy:7)

找不到屬性?我們生成class查看:


groovy_test2.png

結(jié)果一目了然绍刮,num是run方法中的局部變量温圆,當(dāng)然無法在printNum中訪問。

我們?cè)倏床患觗ef的class結(jié)果:


groovy_test3.png

此時(shí)通過callGroovyObjectGetProperty來訪問num變量孩革。

也可以將num變?yōu)槌蓡T變量岁歉,這樣printNum自然就可以訪問了

import groovy.transform.Field; 
@Field num = 1  //在num前面加上@Field標(biāo)注,num就是test的成員變量了膝蜈。
groovy_test4.png
引號(hào):

Groovy支持單引號(hào)锅移,雙引號(hào),三引號(hào)

  • 單引號(hào)中的內(nèi)容嚴(yán)格對(duì)應(yīng)Java的String饱搏,不對(duì)$進(jìn)行轉(zhuǎn)義非剃。
  • 雙引號(hào)的內(nèi)容如果有則會(huì)對(duì)表達(dá)式先求值
  • 三引號(hào)可以指示一個(gè)多行的字符串,并可以在其中自由的使用單引號(hào)和雙引號(hào)推沸。
def name = "Candy"
println 'Hello $name'
println "Hello $name"
println "Hello ${name}"

輸出:

Hello $name
Hello Candy
Hello Candy
函數(shù):

除非指定了確定的返回類型(void也可以作為一種返回值)备绽,否則定義函數(shù)必須加上關(guān)鍵字def。
函數(shù)默認(rèn)最后一行語(yǔ)句的值為函數(shù)返回值鬓催,可以顯式的return返回值肺素。

def method1() {
    return "This is method1"
}

def method2() {
    "This is method2"
}

String method3() {
    return "This is method3"
}

void method4() {
    println "hello method4"
}

println method1()
println method2()
println method3()
println method4()

輸出:

This is method1
This is method2
This is method3
hello method4
null

參數(shù)可以不指定類型,參數(shù)可以有默認(rèn)值:

def printPeople(name, age=18) {
    println "name is ${name}, age is ${age}"
}

printPeople("Jack", 22)
printPeople("Terry")

// 輸出:
// name is Jack, age is 22
// name is Terry, age is 18

當(dāng)函數(shù)有一個(gè)或多個(gè)參數(shù)時(shí)宇驾,調(diào)用的時(shí)候可以不加括號(hào)倍靡,比如:

println "Hello World"  // 等同于 println("Hello World")

def someMethod1(it) {
    println "someMethod1, it=${it}"
}

def someMethod2() {
    println "someMethod2"
}

someMethod1(1) // someMethod1, it=1
someMethod1 2 // someMethod1, it=2

someMethod2() // someMethod2
someMethod2 // 報(bào)錯(cuò)

另外一個(gè)語(yǔ)法特性是Command Chain,不僅可以省略圓括號(hào)课舍,又可以省略”.”號(hào)塌西。比如:a(b).c(d)他挎,可以寫成:a b c d。

def name(name) {
    println "name: $name"
    return this
}
def age(age) {
    println "age: $age"
    return this
}

name "Jerry" age 18

函數(shù)調(diào)用不加括號(hào)在Gradle中有很多運(yùn)用捡需,舉個(gè)栗子:

include ':app'

實(shí)際是函數(shù)調(diào)用:


gradle_include.png
閉包

Groovy閉包文檔:http://www.groovy-lang.org/closures.html

閉包語(yǔ)句:

def clos = { param -> println "${param}" }
clos.call("Hello") // 等同于 clos("Hello")办桨,輸出 Hello

如果閉包沒定義參數(shù)的話,則隱含有一個(gè)參數(shù)it:

def clos = { println "${it}" }
clos("Hello") // 輸出 Hello

當(dāng)然也可以指定閉包沒有參數(shù):

def clos = { -> println "${it}" }
clos("Hello") // 報(bào)錯(cuò)

當(dāng)Closure作為函數(shù)最后一個(gè)參數(shù)時(shí)站辉,可以將Closure拿到括號(hào)外邊崔挖,比如:

def doSomething(arg, Closure clos) {
   print "${arg} "
   clos()
}

doSomething ("Hello", { println 'Candy' })
doSomething "Hello", { println 'Bob' }
doSomething ("Hello") { println 'Tom' }


// 輸出:
// Hello Candy
// Hello Bob
// Hello Tom

閉包在gradle中有大量的運(yùn)用,比如:

repositories {
    jcenter()
}

這在gradle腳本中很常見的寫法庵寞,其實(shí)repositories是一個(gè)函數(shù):

/**
 * Configures the repositories for the script dependencies. Executes the given closure against the {@link
 * RepositoryHandler} for this handler. The {@link RepositoryHandler} is passed to the closure as the closure's
 * delegate.
 *
 * @param configureClosure the closure to use to configure the repositories.
 */
void repositories(Closure configureClosure);

可以看到這個(gè)函數(shù)的參數(shù)是一個(gè)閉包狸相,那么上面的寫法就等同于:

repositories({
   jcenter()
})

至此Groovy暫時(shí)告一段落,我們已經(jīng)了解了一些Groovy的基礎(chǔ)知識(shí)捐川,下一步就是學(xué)習(xí)Gradle了脓鹃。

Gradle

Gradle是一個(gè)框架,它負(fù)責(zé)定義流程和規(guī)則古沥。而具體的編譯工作則是通過插件的方式來完成的瘸右。比如編譯Java有Java插件,編譯Groovy有Groovy插件岩齿,編譯Android APP有Android APP插件太颤,編譯Android Library有Android Library插件。

我們?cè)贏ndroidStudio中創(chuàng)建project時(shí)盹沈,會(huì)默認(rèn)生成一個(gè)多項(xiàng)目結(jié)構(gòu)的Gradle工程:


gradle_dirs.png

上圖三個(gè).gradle結(jié)尾的文件就是Gradle的構(gòu)建腳本龄章。

共有三種不同類型的腳本:


gradle_script_types.png
Init script

我們大部分人并不常用,但是它確實(shí)可以配置:

  • 在命令行里指定: gradle -I 或者 --init-script <pathToInit.gradle>
  • 在USER_HOME/.gradle/init.d目錄下乞封,放置init.gradle文件
Settings script

settings.gradle對(duì)于多項(xiàng)目的Project是必須的做裙,文件在rootProject根目錄下,Settings腳本的Delegate是Settings對(duì)象肃晚,可以看下Settings類都有哪些函數(shù):


gradle_settings_methods.png

我們常用的就是include函數(shù)了:

include ':app'
Build script

build.gradle腳本文件是我們最常編輯的锚贱,絕大部分配置工作都在這里面。每一個(gè)待編譯的Project都對(duì)應(yīng)一個(gè)build.gradle关串,它的Delegate是Project:


gradle_project_des.png

每一個(gè)待編譯Project都有一個(gè)build.gradle拧廊,每一個(gè)build.gradle文件都會(huì)轉(zhuǎn)換成一個(gè)Project對(duì)象,在構(gòu)建的時(shí)候包含一系列的Task晋修。比如一個(gè)Android APK的編譯包含:Java源碼編譯Task吧碾、資源編譯Task、JNI編譯Task飞蚓、lint檢查Task滤港、打包生成APK的Task廊蜒、簽名Task等趴拧。

Task

Task 是Gradle中的一種數(shù)據(jù)類型溅漾,它代表了一些要執(zhí)行的工作。不同的插件可以添加不同的Task著榴。每一個(gè)Task都需要和一個(gè)Project關(guān)聯(lián)添履。

創(chuàng)建Task:

task hello1 {
    doLast {
        println '--- Task hello1 ---'
    }
}

task hello2
hello2.doLast {
    println '--- Task hello2 ---'
}

tasks.create('hello3')
hello3.doLast {
    println '--- Task hello3 ---'
}

使用命令gradle [task]來執(zhí)行task,如果task是駝峰命名時(shí)(比如task名字是helloWorld)脑又,可以使用縮寫 gradle hW暮胧。

task依賴:

task hello {
    doLast {
        println 'Hello World !'
    }
}

task intro() {
    doLast {
        println "I am Gradle"
    }
}
intro.dependsOn(hello)
// 或:
// task intro(dependsOn: hello) { 
//    doLast {
//        println "I am Gradle"
//    }
// }

執(zhí)行gradle -q intro后輸出:

Hello World !
I am Gradle

Android Plugin for Gradle

Android Plugin for Gradle 是通過Gradle插件機(jī)制實(shí)現(xiàn)的Android平臺(tái)的Gradle構(gòu)建插件,詳情請(qǐng)參考:
Android Gradle Plugin User Guide
Android Plugin DSL Reference
Configure Your Build

build.gradle解讀

defaultConfig:

defaultConfig {
    // 包名
    applicationId "com.goodl.gradledemo"
    // 最低版本
    minSdk 9
    // 目標(biāo)版本
    targetSdk 25
    // 版本號(hào)
    versionCode 1
    // 版本名稱
    versionName "1.0"
    // 自動(dòng)化測(cè)試
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    
    ndk {
        abiFilters 'arm64-v8a', 'armeabi-v7a'
    }
    resConfigs "en", "zh"
}

signConfigs:

signingConfigs {
    // debug 簽名
    debug {}
    // release 簽名
    release {
        // 簽名文件所在路徑
        storeFile file("ray.jks")
        // 簽名密碼
        storePassword "111111"
        // 別名
        keyAlias "rayhahah"
        keyPassword "111111"
        v2SigningEnabled true
    }
    // 自定義簽名配置
    ray {
        // 和上面的屬性一致问麸,根據(jù)個(gè)人需求實(shí)現(xiàn)不同配置
    }
}

buildTypes:

buildTypes {
    debug {
        minifyEnabled false
        signingConfig signingConfigs.debug
        buildConfigField 'boolean', 'ENV_PRODUCTION', "false"
    }

    uat {
        initWith debug // 從給定的構(gòu)建類型復(fù)制所有屬性
        buildConfigField 'boolean', 'ENV_PRODUCTION', "false"
    }
    
    release {
        minifyEnabled true // 是否混淆
        zipAlignEnabled true // zipAlign優(yōu)化
        shrinkResources true // 移除無用的resource文件
        signingConfig signingConfigs.release // 簽名
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        buildConfigField 'boolean', 'ENV_PRODUCTION', "true"
    }
}

sourceSets:


sourceSets {
    // 這樣的配置適用于將 Eclipse 中的項(xiàng)目結(jié)構(gòu)遷移到 AndroidStudio 中
    main {
        // 指定src資源目標(biāo)目錄
        java.srcDirs = ['src']
        // 指定asset的目標(biāo)目錄
        assets.srcDirs = ['assets']
        // 指定res的目標(biāo)目錄
        res.srcDirs = ['res']
        // 指定依賴C文件的目標(biāo)目錄
        jni.srcDirs = ['jni']
         // 指定依賴so文件的目標(biāo)目錄
        jniLibs.srcDirs = ['libs']
        // 指定Manifest的目標(biāo)文件路徑
        manifest.srcFile 'AndroidManifest.xml'
    }
 }

productFlavors:

// 多渠道打包配置
productFlavors {
    xiaomi {
        manifestPlaceholders = [CHANNEL_VALUE: "xiaomi"]
    }
    myapp {
        manifestPlaceholders = [CHANNEL_VALUE: "myapp"]
    }
}

lintOptions:

lintOptions {
  // 啟用出錯(cuò)停止grgradle構(gòu)建
  abortOnError false
  // true -- 檢查所有問題點(diǎn)往衷,包含其他默認(rèn)關(guān)閉項(xiàng)
  checkAllWarnings true
  // 關(guān)閉指定問題檢查
  disable 'TypographyFractions', 'TypographyQuotes'
  // 打開指定問題檢查
  enable 'RtlHardcoded', 'RtlCompat', 'RtlEnabled'
  // 僅檢查指定問題
  check 'NewApi', 'InlinedApi'
  // true -- 生成HTML報(bào)告(帶問題解釋,源碼位置严卖,等)
  htmlReport true
  // html 報(bào)告可選路徑(構(gòu)建器默認(rèn)是lint-results.html )
  htmlOutput file("lint-report.html")
  // 忽略指定問題的規(guī)則(同關(guān)閉檢查)
  ignore 'TypographyQuotes'
}

重命名 apk 名稱:

static def releaseTime() {
    return new Date().format("yyyyMMdd", TimeZone.getTimeZone("UTC"))
}

android {
  defaultConfig {
    // ...
  }

  // ...

  applicationVariants.all { variant ->
    variant.outputs.all {
        outputFileName = "myapp_v${defaultConfig.versionName}_${releaseTime()}_${variant.name}.apk"
    }
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末席舍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子哮笆,更是在濱河造成了極大的恐慌来颤,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稠肘,死亡現(xiàn)場(chǎng)離奇詭異福铅,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)项阴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門滑黔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人环揽,你說我怎么就攤上這事拷沸。” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵贪绘,是天一觀的道長(zhǎng)销钝。 經(jīng)常有香客問我,道長(zhǎng)序无,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任衡创,我火速辦了婚禮帝嗡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘璃氢。我一直安慰自己哟玷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巢寡,像睡著了一般喉脖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抑月,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天树叽,我揣著相機(jī)與錄音,去河邊找鬼谦絮。 笑死题诵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的层皱。 我是一名探鬼主播性锭,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼叫胖!你這毒婦竟也來了篷店?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤臭家,失蹤者是張志新(化名)和其女友劉穎疲陕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钉赁,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蹄殃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了你踩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诅岩。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖带膜,靈堂內(nèi)的尸體忽然破棺而出吩谦,到底是詐尸還是另有隱情,我是刑警寧澤膝藕,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布式廷,位于F島的核電站,受9級(jí)特大地震影響芭挽,放射性物質(zhì)發(fā)生泄漏滑废。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一袜爪、第九天 我趴在偏房一處隱蔽的房頂上張望蠕趁。 院中可真熱鬧,春花似錦辛馆、人聲如沸俺陋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)腊状。三九已至诱咏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寿酌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工硕蛹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留醇疼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓法焰,卻偏偏與公主長(zhǎng)得像秧荆,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子埃仪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容