Gradle學(xué)習(xí)總結(jié)

本篇主要是個(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
配置方式如下:

grovvy1.png

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集合,閉包,映射

  1. 范圍 :for (i in 0..4) 將包含的范圍限制在0-4間。
    
  2. 集合。def coll = ["Groovy", "Java", "Ruby"]   coll << “smalltalk”
    
  3. 映射  def hash = [“name”:”andy”, “vip”:”45”]
    
  4. 閉包  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類。

grovvy2.png

實(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工作流程

gradle1.png

首先是初始階段,對(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

gradle2.png

六.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. 插件

gradle3.png

2. 各Script配置

android中的BuildScriptBlock

gradle4.png

3. gradle命令及依賴配置

依賴配置有:模塊依賴,本地依賴,遠(yuǎn)程依賴。

gradle5.png

命令:頂級(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)建方案

  1. facebook buck https://buckbuild.com/
    
  2. 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
}

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

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

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