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

本篇主要是個人學(xué)習(xí)gradle的筆記總結(jié)

一.開始之前

1. 為什么學(xué)習(xí)Gradle

  • 采用DSL(Domain Specific Language)語言來描述和控制構(gòu)建邏輯。(所謂領(lǐng)域?qū)S谜Z言,其基本思想是“求專不求全”,不像通用目的語言那樣目標(biāo)范圍涵蓋一切軟件問題,而是專門針對某一特定問題的計算機語言。)
  • 支持Maven或者Ivy的依賴管理。
  • 插件可以提供自己的DSL和API供文件使用。
  • 讓代碼或資源復(fù)用更容易。
  • 讓創(chuàng)建同一應(yīng)用程序的不同版本變得更加容易,無論是多個 apk 發(fā)布版本還是同一個應(yīng)用的不同定制版本。

2. 學(xué)習(xí)條件

由于Android最新推薦的編譯方式是采用Gradle進(jìn)行編譯,因此我們必須學(xué)習(xí)Gradle的基本知識, 而Gradle是基于Groovy語法編寫的,因此我們必須了解Groovy語法 ,而Groovy又是基于Java語言的,是java語言的擴展, 所以說只要我們會java語言就可以編寫出Gradle腳本。

3. 學(xué)習(xí)目標(biāo)

A: 學(xué)習(xí)Groovy基本概念, 學(xué)習(xí)Groovy集合, 類,和閉包語法。
B: 學(xué)習(xí)Gradle 基本用法, 能夠編寫task,理解project概念。
C: 可以理解android的編譯配置。

二.Groovy初探

1. 什么是Groovy

Grovvy是JVM的一個替代語言, 是指可以用Groovy在Java平臺上進(jìn)行Java編程,使用方式基本和Java代碼先同,它也是Java的擴展。

2. 開發(fā)環(huán)境配置

http://www.groovy-lang.org/learn.html
配置方式如下:

grovvy1.png

3. Groovy的特性

Groovy松散的java語法允許省略分號和修飾符;除非另指定,groovy所有內(nèi)容都為public;允許定義簡單腳本,無需定義正規(guī)的class對象;允許省略變量類型。

4. Groovy和java的對比

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}
    

如果閉包沒有定義參數(shù),那么隱含一個參數(shù)it, 和this作用類似。

對于閉包的參數(shù)需要查看api文檔。

6. 類

Groovy類就是java類,初始化時Groovy 自動提供一個構(gòu)造函數(shù),構(gòu)造函數(shù)接受一個名稱-值對的映射,這些名稱-值對與類的屬性相對應(yīng)。這是 Groovy 的一項開箱即用的功能 — 用于類中定義的任何屬性,Groovy 允許將存儲了大量值的映射傳給構(gòu)造函數(shù)。還會對成員變量定義get,set方法,所以可以直接點引用。

7. 看具體例子。

Demo地址:

Groovy API文檔為: http://www.groovy-lang.org/api.html

三.Groovy深入

1. Groovy和JVM的關(guān)系

除了語言和Java相通外,Groovy有時候又像一種腳本語言。當(dāng)我執(zhí)行Groovy腳本時,Groovy會先將其編譯成Java類字節(jié)碼,然后通過Jvm來執(zhí)行這個Java類。

grovvy2.png

實際上,由于Groovy Code在真正執(zhí)行的時候已經(jīng)變成了Java字節(jié)碼,所以JVM根本不知道自己運行的是Groovy代碼.

2. 腳本和類

既然是基于java來執(zhí)行的,那我們將groovy轉(zhuǎn)會為java類。

執(zhí)行 groovyc-d classes test.groovy

groovyc是groovy的編譯命令,-d classes用于將編譯得到的class文件拷貝到classes文件夾下。

我們可以看到helloworld.groovy 被轉(zhuǎn)化為java class類,繼承自Script。

Song 類被轉(zhuǎn)化為實現(xiàn)GroovyObject的類,并封裝了get,set方法。

可以看出, groovy既可以作為類來使用, 又可以作為腳本來使用。

Groovy 腳本的代碼其實都會被放到run函數(shù)中執(zhí)行的。變量作用域,區(qū)分def和不用def定義, def定義的為run函數(shù)作用域,無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, 無需關(guān)閉輸入流, 閉包會自動關(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

用戶手冊:https://docs.gradle.org/current/userguide/userguide.html

API文檔:https://docs.gradle.org/current/javadoc/

DSL手冊:https://docs.gradle.org/current/dsl/

Gradle 是配置編譯腳本, 也是編程開發(fā)框架。

Gradle 由一個或多個proejct組成, 每一個待編譯的工程都叫一個project,每一個project在構(gòu)建的時候都包含一系列的task。比如一個Android apk的編譯可能包含:java源碼編譯Task,資源編譯Task,JNI編譯Task,lint檢查Task, 打包生成APK的task,簽名Task等等。一個project到底包含多少Task,其實是由編譯腳本指定的插件決定。插件就是用來定義Task,并且具體執(zhí)行這些task的東西。

2. 開發(fā)環(huán)境配置

http://gradle.org/ 下載對應(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í)行或者要干的工作。每一個task都要和一個project關(guān)聯(lián)。

創(chuàng)建一個task

task hello {

 doLast{

    println “hello gradle”

 }

}

或者

task hello << {

   println “hello gradle”

}

參考Demo: 2-define-task, 3-undertand-gradle-syntax,

4. Project

每一個build.gradle 就是一個project。一個project中會有一個或多個task。

5. 屬性

(Demo: 5-define-custom-properties)

gradle 的project中會有一些默認(rèn)的屬性,可以理解為類的成員變量,我們還可以給project增加一些額外屬性, 通過ext。

6. 依賴關(guān)系

(Demo: 7-dependency-management, 8-multi-project)
project依賴可以通過dependencies{}來設(shè)置依賴,task依賴可以通過dependsOn 來設(shè)置。

7. Multi-Project組織

(Demo: 8-multi-project)

多project管理,主要依靠settings.gradle ,在該文件中include 需要編譯的模塊名即可??梢栽谠撐募性黾雍瘮?shù),如initGradleEnvironment(),這些函數(shù)會在gradle構(gòu)建整個工程任務(wù)的時候執(zhí)行。其實include也是函數(shù)。

8. Gradle命令

gradle projects

gradle tasks

gradle taskname 執(zhí)行任務(wù)

gradle clean, assembleDebug, aR, build …

五.Gradle深入

1. Gradle工作流程

gradle1.png

首先是初始階段,對于multi-project而言,就是settings.gradle配置執(zhí)行。

其次是配置階段,該階段解析每個project中的build.gradle。這兩個階段我們可以增加hook進(jìn)來,執(zhí)行相關(guān)任務(wù)。

最后是執(zhí)行階段,執(zhí)行完成后我們也可以增加hook。

Gradle基于groovy,所以編譯執(zhí)行時gradle也會把腳本轉(zhuǎn)化為java對象。

Gradle中主要有三種對象, 這三種對象和三種不同的腳本文件對應(yīng), 在Gradle執(zhí)行的時候,會將腳本轉(zhuǎn)化成對應(yīng)的對象,分別為Gradle對象,Project對象,Settings對象。

2. Gradle編程模型及生命周期

https://docs.gradle.org/current/userguide/build_lifecycle.html

3. Gradle對象

當(dāng)我們執(zhí)行g(shù)radle xxx時,Gradle會從默認(rèn)的配置腳本中構(gòu)造出一個Gradle對象, 在整個執(zhí)行過程中只有這么一個對象, Gradle對象的數(shù)據(jù)類型就是Gradle。我們一般很少去定制這個腳本

4. Settings對象

settings.gradle 會被轉(zhuǎn)化為一個Settings對象。

5. Project對象

每個build.gradle 都會轉(zhuǎn)化為一個Project對象。由于project對應(yīng)到具體工程, 因此要為project加載對應(yīng)的插件。其實每一個project具體包含多少個task是由插件決定的。

6. 代理機制

Gradle大量地使用了Groovy閉包的delegate機制。簡單來說,delegate機制可以使我們將一個閉包中的執(zhí)行代碼的作用對象設(shè)置成任意其他對象。3-undertand-gradle-syntax。

apply方法, 使用: apply plugin: ‘com.android.library’, 如果編譯lib,則使用此插件, 也可以加載一個gradle文件,如: apply from : ‘utils.gradle’。

此處可以utils.gradle定義的屬性和方法。為什么可以使用呢? 我們知道gradle和groovy一樣,每個腳本都繼承自Script。utils.gradle 也會被轉(zhuǎn)化為一個Script對象, Script對象中有一個delegate對象, 在apply函數(shù)中有個from參數(shù),還有個to參數(shù), 通過to參數(shù)可以將delegate對象指向別的東西,這里即utils 的Scrpit類, 當(dāng)你在自己的Script中操作一些不是自己定義的變量或者函數(shù)時,gradle會到Script的delegate對象中去找。

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

命令:頂級命令有4個

  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的高級配置

1. 構(gòu)建變種版本-BuidVariant。

構(gòu)建類型+定制產(chǎn)品=構(gòu)建變種版本

BuildType + ProductFlavor 任何一種組合都會是一個版本。

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
            }
        }
    }
每一個Variant也會創(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)先級,并允許在Variant的層次上做一些定制。

2. 高級構(gòu)建選項

  android {
        aaptOptions {
            noCompress 'foo', 'bar'
            ignoreAssetsPattern "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"
        }
    }

aapt操作配置將應(yīng)用到所有task上。

3. 操作Task

Android項目中會有大量的task, 并且他們基于buildType和productFlavor生成的task, 從而我們直接引用到該task的編譯成java后的class,為了解決該問題, android對象提供了三個Collection屬性:

applicationVariants(只適用于app plugin)

libraryVariants(只適用于library plugin)

testVariants(兩個plugin都適用)

這三個collection中的任意一個都會觸發(fā)生成所有對應(yīng)的task。

android.applicationVariants.each { variant ->
        ....
    }

http://wiki.jikexueyuan.com/project/android-gradle-guide/advanced-build-customization.html

通過這些變量和方法可定制對應(yīng)的功能。

官方詳細(xì)文檔:http://tools.android.com/tech-docs/new-build-system/user-guide

八.Gradle自定義Task和Plugin

Gradle本身只是一個架子,真正起作用的是Task和Plugin。要真正了解Task和Plugin的工作機制并熟練運用,學(xué)會自定義Task類型和Plugin是大有裨益的。

1. 自定義Task。

Gradle中的Task要么是由不同的Plugin引入的,要么是我們自己在build.gradle文件中直接創(chuàng)建的。在默認(rèn)情況下,我們所創(chuàng)建的Task是DefaultTask類型,該類型是一個非常通用的Task類型,而在有些時候,我們希望創(chuàng)建一些具有特定功能的Task,比如Copy和Jar等。

Demo: 9-custom-task, 分為三部分,1,在build.gradle中直接定義Task,2.在當(dāng)前工程中定義Task,3.在單獨的項目中定義Task,將其上傳maven庫中,其他項目來引用。

2. 自定義Plugin。

在Plugin中,我們可以向Project中加入新的Task,定義configurations和property等。我們3種方法可以自定義Plugin,這些方法和自定義Task類型的3種方法相似。

Demo:10-custom-plugin 每一個自定義的Plugin都需要實現(xiàn)Plugin<T>接口,事實上,除了給Project編寫Plugin之外,我們還可以為其他Gradle類編寫Plugin。該接口定義了一個apply()方法,在該方法中,我們可以操作Project,比如向其中加入Task,定義額外的Property等。

九.通過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庫


apply plugin:‘maven’

uploadArchives{

repositories.mavenDeployer{

   repository(url: ‘file:../lib’)

}

}

使用時

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無需定義類型
def repeat(val) {
   /*for(i = 0; i < 5; i++) {
      println val;
   }*/
   //groovy中的范圍, 0..5 是一個集合
   /*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. 插入符號, 集合操作。
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ù)的最后一個參數(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)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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