Android studio:Groovy 與 Gradle 基礎【三】


轉載:http://www.apkbus.com/forum.php?mod=viewthread&tid=255064&extra=page%3D2%26filter%3Dauthor%26orderby%3Ddateline&_dsign=276e9e2e

 
相信看過前一篇  《Android Studio 與 Gradle 深入》 的同學,有一部分就會遇到我初識 Gradle 時的困惑:代碼我也依稀看得懂,但就是不知道還能這樣寫,為什么這樣寫。
 

問題與解決方案

回想我在 Gradle 的學習過程中遇到的問題與及其解決方案,總結出下面三點:
 
  • 原理不懂:學習 Groovy 與 Gradle 的基礎原理
  • Gradle 實踐不懂:學會找示例,學習開源例子
  • 方法和屬性不懂:學會查文檔

 

下面的我將以解決三個問題為線索,介紹 Groovy 和 Gradle。
 

學習 GroovyGroovy 概述

Gradle 采用了 Groovy 語言作為主要的腳本語言。一個 build.gradle 文件,其實是一個 Groovy 類。
Groovy 是一個基於 JVM 的語言,代碼最終編譯成字節碼(bytecode)在 JVM 上運行。它具有類似於 Java 的語法風格,但是語法又比 Java 要靈活和方便,同時具有動態語言(如 ruby 和 Python)的一些特性。
Groovy 的諸多特定,很適合用來定義 DSL(Domain Specific Language)。
簡單的來講 DSL 是一個面向特定小領域的語言,如常見的 HTML、CSS 都是 DSL,它通常是以配置的方式進行編程,與之相對的是通用語言(General Purpose Language),如 Java 等。
既然是一門語言,就肯定有自己的特性。我們要從下面幾個步驟來介紹 Groovy:
 
  • 環境安裝
  • 語言基礎
  • 語言特性及其本質


環境安裝

Groovy 官方安裝文檔提供多種方式進行安裝,確保你不會在跪在環境配置的路上 ^-^ :
  • Windows 下推薦 binary 包配置環境變量
  • Mac 下推薦使用 sdkman 或者 Brew 進行安裝
  • Linux 下推薦 sdkman
  • 嵌入在程序中,則推薦使用 Maven 遠程依賴

 

初學者也沒有必要使用IDE,平添障礙,后期用 Intellij IDEA Community 版本足矣。
 
下面只介紹 Mac 下使用 sdkman 的安裝方式。
 
  • 下載安裝 sdkman,執行下面命令,按照提示安裝即可
1 $ curl -s http://get.sdkman.io | bash

 

  • 使環境變量生效

 

1 $ source "$HOME/.sdkman/bin/sdkman-init.sh"

 

  • 安裝 Groovy

 

1 $ sdk install groovy

 

  • 查看當前版本,如果能否運行且輸出對應版本,就是成功了

 

1
2
$ groovy -version
Groovy Version: 2.4.4 JVM: 1.8.0_25 Vendor: Oracle Corporation OS: Mac OS X


初探

安裝好環境之后,先來一個 hello, world!
 
  • 新建文件

 

1 $ vim test.groovy

 

  • 在其中寫上內容

 

1 println "hello, world!"

 

  • 保存退出,執行

 

1
2
$ groovy test.groovy
hello, world!

 

Wow, So easy!
 

語言基礎

下面將會用一些實際的例子,介紹一些最重要的點,
 
例子都已經傳到 github 的 demo 項目中。

第一次使用 demo 項目的時候,需要等待自動下載幾個遠程包。
筆者一個 Java 程序員,可以你能夠看到很多 Java 的習性還是留在代碼中。
 

文件與類,變量與函數

Groovy 代碼文件,支持不顯式聲明類:
 
ScriptClass.groovy
 
1 println 'hello,world'

 

這樣一個 Groovy 腳本,經過編譯之后,會產生一個繼承自 groovy.lang.Script 類的子類:
 
是不是能看出點什么?
 
groovy/build/classes/main/io/kvh/as/groovy/ScriptClass.class
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ScriptClass extends Script {
    public ScriptClass() {
        CallSite[] var1 = $getCallSiteArray();
    }

    public ScriptClass(Binding context) {
        CallSite[] var2 = $getCallSiteArray();
        super(context);
    }

    public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[0].call(InvokerHelper.class, ScriptClass.class, args);
    }

    public Object run() {//關鍵方法
        CallSite[] var1 = $getCallSiteArray();
        return var1[1].callCurrent(this, "hello,world");// got it?
    }
}

 

Groovy 支持如下的方式來定義變量和函數:
 
VarAndMethod.groovy
1
2
3
4
5
6
7
8
9
10
def varAndMethod() {
    def a = 1//不顯式聲明變量類型
    a = "abc"//運行時改變類型

    println a//無需;結束一行代碼
    a = 4//最后一行作為返回值
}
def ret = varAndMethod()//文件內運行方法

println ret//輸出4


字符串

Groovy 支持單引號,雙引號,三單引號聲明一個字符串;
 
Quoted.groovy
1
2
3
4
5
6
7
8
9
10
def quoted() {
        def singleQ = 'hello, single quot'// 聲明為java.lang.String
        def doubleQ = "hello, double quot ${singleQ}"// 如果有${},則為groovy.lang.GString,支持變量替換;否則為java.lang.String
        def tripleQ = '''hello,
triple quot'''// 允許多行,而不需要+號

        println singleQ
        println doubleQ
        println tripleQ
}

 

Groovy 還支持以:
 
1
2
3
"""..."""
/.../
$/.../$
來聲明字符串,詳情參見參考文檔。
 

List,Array 和 Map

Groovy 默認使用 java.util.ArrayList 來提供 List 服務,但提供了更加靈活易用的操作方式:
 
Collections.groovy
 
1
2
3
4
5
6
7
def playList() {
    def lst = ["a",2,true]//支持不同類型元素

    println(lst)
}

playList()

 

要使用 Array,需要顯式聲明:
 
1
2
3
4
5
6
7
8
9
def playArray() {
    def intArr = [1, 2, 3] as int[]//顯示聲明
    String[] strArr = ["a", "b"]//另外一種方式

    println(intArr)
    println(strArr)
}

playArray()

 

使用 key:value 的方式定義 Map,注意 key 的正確使用方式:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def playMap() {
    def map = [a: "a", b: "b"]
    
    println(map)
    
    def key = "name"
    def map2 = [key: 'a']//未使用
    def map3 = [(key): 'a']//使用

    println(map2)
    println(map3)
}

playMap()


import

Groovy 提供了更強大的 import
 
  • 默認 import,這些類都是被默認 import 到代碼中的,可以直接使用

 

1
2
3
4
5
6
7
8
import java.lang.*
import java.util.*
import java.io.*
import java.net.*
import groovy.lang.*
import groovy.util.*
import java.math.BigInteger
import java.math.BigDecimal

 

  • import alias

 

引入一個類,通過 as 關鍵字賦予一個別名,有點 JavaScript 的意思么?
 
Import.groovy
 
1
2
3
import java.lang.String as KString

println(new KString("aaa"))


語言特性及其本質Closure(閉包)

閉包的概念不再贅述,大概就是可以將函數作為參數傳遞和使用,詳情參見 wikipedia
 
1 { [closureParameters -> ] statements }

 

可以省略方括號內的內容,也就是說,可以沒有參數列表。
 
Closure.groovy
 
當閉包不聲明參數列表,默認參數是 it;閉包被定義之后,是一個 Closure 對象,可以對其調用 call 方法使其執行。
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def defaultIt() {
    3.times {
        println it //默認參數 it
    }
}

defaultIt()
def closureObj() {
    def obj = { a ->
        ++a
    }
    println obj.call(1)
}

closureObj()


面向對象特性

  • 定義和實例化一個類
GroovyCLass.groovy
 
1
2
3
4
5
6
7
class People{
    String name
    int age
}

People p1 = new People();
People p2 = new People(name:"Luis",age: 29)//通過類似 map 的方式賦值參數

 

  • 方法的默認參數

 

1
2
3
4
5
6
def foo(String p1, int p2 = 1) {
    println(p1)
    println(p2)
}

foo("hello")

 

  • Field 和 Property

 

Field 是以各種修飾符修飾的變量。Property是私有變量和自帶的 gettters/setters,
下面的類具有私有變量 name、age,並自帶這兩個變量的 getter 和 setter:
 
1
2
3
4
class People{
    String name
    int age
}

 

當變量聲明為 final 的時候,默認就沒有 setter
 
  • Trait

 

Groovy 提供了一個叫做 Trait 特性實現了多繼承,還有很多強大的功能,讀者可以自己探索。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
trait Fly {
    void fly() {
        println("fly")
    }
}

trait Walk {
    void walk() {
        println("walk")
    }
}

class Duck implements Fly, Walk {

}

Duck duck = new Duck()
duck.fly()
duck.walk()


Groovy 基礎小結

至此,我們已經熟悉了 Groovy 的基本語法和特性,相信你也能夠使用 Groovy 寫一些基礎程序了。Groovy 還有很多深入的內容,請用到的時候,參考這本這個 pdf: 《Programming Groovy 2》
下面開始介紹使用 Groovy 寫 Gradle 程序,主要的內容來自 《Gradle Sser Guide》
 

學習 GradleGradle 安裝

三種方式安裝 Gradle:
1 brew install gradle

 

  • 推薦:使用 IntelliJ IDEA(Android Studio)自帶的 wrapper 結構來下載 Gradle

 

Wrapper 是為了讓不同版本的插件能夠使用其對應版本的 Gradle 的一個機制
Gradle Wrapper 會把不同的版本 Gradle 安裝在:
 
1 $USER_HOME/.gradle/wrapper/dists

Gradle Build 的生命周期

回憶一下 《Android Studio 與 Gradle 深入》 中的 Android Studio 項目文件結構:
 
 
1
2
3
4
5
6
7
8
.
├── app                                                //app module
│   ├── build.gradle                //app module 的 build.gradle
├── build.gradle                        //項目 build.gradle,通常配置項目全局配置,如 repositories 和 dependencies
├── gradle.properties                //項目屬性文件,通常可以放置一些常量
├── lib                                                //lib module
│   ├── build.gradle                //lib module 的 build.gradle
└── settings.gradle                        //項目總體設置,通常是配置項目中所有的 module


Gradle 構建三個階段:

  • 初始化:Gradle 支持單 module 構建和多 module 構建(Android Studio 創建的項目默認是多 module)。初始化階段,Gradle 會為每一個 module 中的 build.gradle 文件創建一個 Project 實例。
  • 配置:項目根目錄的 build.gradle 會首先被執行
  • 執行:執行所選取的 task

Settings.gradle

多 module 構建要求在項目根目錄下有一個 settings.gradle,用來指定哪些 module 參與構建,如:
settings.gradle
 
1
2
3
include ':app', ':groovy'

println 'print in settings.gradle'

 

在 settings.gradle 文件中,添加一行打印語句,在控制台中,切換到當前項目根目錄下執行:
 
1 ./gradlew -p groovy build

 

可以在看出 settings.gradle 的代碼每次都會率先執行。
 

Task

接下來,我們開始學習 Gradle 的核心 Task。
groovy/build.gradle
定義一個 Task:
 
1
2
3
4
5
task hello {
    doLast {
        println 'Hello,'
    }
}

 

執行命令,查看輸出:
 
1
2
$ ./gradlew hello
Hello,

 

Task 也可以這樣定義:
 
1
2
3
task World << {
    println 'World!'
}

 

注意,如果定義成這樣:
 
1
2
3
task hi {
    println 'description hi'
}

 

在進行初始化和配置的時候,下面語句就會運行。
 
1 println 'hi'

 

這種語法通常是用來定義 task 的描述信息。
Task 可設置 dependsOn 和 finalizedBy:
 
1
2
3
4
5
6
7
8
9
10
11
task hello {
    doLast {
        println 'Hello,'
    }
}

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

World.finalizedBy hello

 

執行 intro 之前,會先執行 hello;執行 World 之后,會自動執行 hello


Plugin

Gradle 的核心代碼,只提供了一個框架,具體的功能(如構建 Android 工程)是通過插件機制來實現的。
Gradle 提供了大量官方的插件,如 Maven、Groovy、Java、Publishing、Signing等,也有大量第三方的插件(Android),甚至每個人都可以自己實現一個插件(如 筆者開發的 Bugtags 插件,這個將在最后一篇講述)。
這些 plugin 定義了一系列的 task、DSL 和約定,在build.gradle 文件使用這些 plugin:
 
1 apply plugin: java

 

當你寫了一個獨立的 file_uri.gradle 文件,你可以通過:
 
1 apply from: 'file_uri.gradle'

 

來引入你的 gradle 文件,這個文件甚至可以在某個服務器上。
 

Gradle 實踐參考

學習了基礎理論之后,如果你還是不知道如何開始寫,那就先來實現一個自定義 apk 名稱的功能吧!
 
1
2
3
4
5
6
7
8
9
10
11
12
13
android.applicationVariants.all { variant ->//獲取 variant 參數,就是 productFlavor x buildType
    variant.outputs.each { output ->//獲取輸出文件
        def file = output.outputFile//修改實例
        output.outputFile = new File(
                (String) file.parent,
                (String) file.name.replace(
                        file.name,
                        // alter this string to change output file name
                        "Your_Apk_Name_" + variant.name + "_" + variant.versionName + ".apk"
                )
        )
    }
}

 

你問我怎么知道 android 下有個 applicationVariants?其實我也不知道的,也得找文檔。
 
因為使用的是 Android 的插件,那就得在谷歌搜 “android gradle plugin dsl”,果然有個 Android Plugin DSL Reference
 
點進去找找,里面有關於 build variant 的文檔: applicationVariants,既然是一個 Set,那就可以調用 all 方法。
 
寫代碼調試,再配合文檔,你就曉得該怎么寫了。
 

總結

從 Gradle 入門到現在略懂,經歷了大量懵懂的時光。最后狠下心去系統學習了 Groovy 和 Gradle 的基礎之后,最終茅塞頓開。希望讀者遇到類似的情況,一定要沉下心,多學多練。
在接下來的兩篇,我將分別介紹將發布遠程庫和編寫 Gradle 插件。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM