《Android Gradle權威指南》之Gradle入門

非同步社群發表於2019-03-04

本文摘自人民郵電出版社非同步社群《Android Gradle權威指南》一文

非同步社群試讀地址:www.epubit.com.cn/book/detail…

第1章 Gradle入門

Gradle是一款非常優秀的構建系統工具,它的DSL基於Groovy實現,可以讓你很方便地通過程式碼控制這些DSL來達到你構建的目的。Gradle構建的大部分功能都是通過外掛的方式來實現,所以非常靈活方便,如果內建外掛不能滿足你的需求你可以自定義自己的外掛。

本章我們就介紹Gradle的入門知識,在介紹之前,我們先假定讀者已經具備以下知識。

(1)瞭解並且會使用Java,精通最好。

(2)會獨立搭建Java開發環境。

(3)最好會使用Linux作業系統,比如Ubuntu。

為什麼會有這樣的假定呢?因為這本書是介紹Android Gradle開發構建的書,所以不會講Java的基本知識。希望讀者會用Linux作業系統的原因,是因為本書的所有指令碼、程式碼、IDE等都是基於Ubuntu完成的,當然比如涉及Gradle安裝還會介紹一下Windows的安裝步驟,但是不會太多涉及Windows的東西,所以還是希望讀者在閱讀本書前已經掌握了這些知識。

1.1 配置Gradle環境

安裝之前確保已經安裝配置好Java環境,要求JDK 6以上,並且在環境變數裡配置了JAVA_HOME,檢視Java版本可以在終端輸入如下命令:

java –version複製程式碼

我這裡使用的是open jdk 1.8.0_91:

➜  ~ java -version
openjdk version "1.8.0_91"
OpenJDK Runtime Environment (build 1.8.0_91-8u91-b14-3ubuntu1~16.04.1-b14)
OpenJDK 64-Bit Server VM (build 25.91-b14, mixed mode)複製程式碼

1.1.1 Linux下搭建Gradle構建環境

這裡以Ubuntu 16.04發行版為例介紹如何在Linux下搭建Gradle構建環境,其他諸如CentOS大同小異,參考一下就可以了。

我們這裡以Gradle 2.14.1版本為準進行介紹。先到Gradle官網gradle.org/ 下載好Gradle SDK,直接下載地址為downloads.gradle.org/distributio… SDK所有相關的內容,包括原始碼、文件、示例等。如果因為網路問題下載不了,可以使用映象下載,映象首頁為mirrors.flysnow.org/,該Gradle版本下…

① docs:API、DSL、指南等文件。

② getting-started.html:入門連結。

③ init.d:gradle的初始化指令碼目錄。

④ lib:相關庫。

⑤ LICENSE。

⑥ media:一些icon資源。

⑦ NOTICE。

⑧ samples:示例。

⑨ src:原始檔。

要執行Gradle,必須把GRADLE_HOME/bin目錄新增到你的環境變數PATH的路徑裡才可以。在Linux下,如果你只想為當前登入的使用者配置可以執行Gradle,那麼可以編輯~/.bashrc檔案新增以下內容:

#這裡是作者的Gradle目錄,要換成你自己的
GRADLE_HOME=/home/flysnow/frame/gradle

PATH=${PATH}:${GRADLE_HOME}/bin
Export GRADLE_HOME PATH複製程式碼

上面GRADLE_HOME是我的Gradle解壓後的目錄,這裡要換成你自己的。以上新增後儲存,然後在終端輸入source ~/.bashrc,回車執行讓剛剛的配置生效。

如果你想讓所有使用者都可以使用Gradle,那麼你就需要在/etc/profile中新增以上內容,在這裡新增後,對所有使用者都生效,這種方式的新增,必須要重啟計算機才可以。

好了,現在我們已經配置好了,要驗證我們的配置是否正確,是否可以執行Gradle,我們只需要開啟終端,輸入gradle -v命令檢視即可。如果能正確顯示Gradle版本號、Groovy版本號、JVM等相關資訊,那麼說明你已經配置成功了。這裡以驗證我的配置為例:

➜  ~ gradle -v
------------------------------------------------------------
Gradle 2.14.1
------------------------------------------------------------

Build time: 2016-10-20 03:46:36 UTC
Build number: none
Revision: b463d7980c40d44c4657dc80025275b84a29e31f

Groovy: 2.4.4
Ant: Apache Ant(TM) version 1.9.3 compiled on December 23 2013
JVM: 1.8.0_91 (Oracle Corporation 25.91-b14)
OS: Linux 4.4.0-38-generic amd64複製程式碼

1.1.2 Windows下搭建Gradle構建環境

Windows下搭建Gradle環境和Linux非常相似,只不過方式不同。我們通過右擊我的電腦,開啟屬性皮膚,然後找到環境變數配置項,新增GRADLE_HOME環境變數,然後把GRADLE_HOMEin新增到PATH系統變數裡儲存即可。完成後開啟CMD,執行gradle -v來進行驗證,整體效果和Linux差不多,這裡就不再一一詳述。

1.2 Gradle版Hello World

環境搭建好了,那麼我們就開始寫一個Hello World版的Gradle指令碼。

新建好一個目錄,我這裡是android-gradle-book-code,然後在該目錄下建立一個名為build.gradle的檔案,開啟編輯該檔案,輸入以下內容:

task hello{
doLast{
println`Hello World!`
}
}複製程式碼

開啟終端,然後移動到android-gradle-book-code下,使用gradle -q hello命令來執行構建指令碼:

$ gradle -q hello
Hello World!複製程式碼

好了,如願以償地列印出來我們想要的結果,下面我們一步步分析結果產生的步驟和原因。build.gradle是Gradle預設的構建指令碼檔案,執行Gradle命令的時候,會預設載入當前目錄下的build.gradle指令碼檔案。熟悉Ant的讀者感覺和build.xml差不多,當然你也可以通過 -b引數指定想要載入執行的檔案。

這個構建指令碼定義一個任務(Task),任務名字叫hello,並且給任務hello新增了一個動作,官方名字是Action,閱讀Gradle原始碼你會到處見到它,其實它就是一段Groovy語言實現的閉包。在這裡我覺得叫業務程式碼邏輯或者回撥實現更貼切一些,因為doLast就意味著在Task執行完畢之後要回撥doLast的這部分閉包的程式碼實現。

熟悉Ant的讀者,會覺得任務(Task)和Ant裡的Target(目標)非常相似。其實沒錯,現在可以認為它們基本上相同。

再看gradle -q hello這段執行命令,意思是要執行build.gradle指令碼中定義的名為hello的Task,-q引數用於控制gradle輸出的日誌級別,以及哪些日誌可以輸出被看到。

看到println `Hello World!`了嗎,它會輸出Hello World!,通過名字相信大家已經猜出來了,它其實就是System.out.println(“Hello World!”)的簡寫方式。Gradle可以識別它,是因為Groovy已經把println()這個方法新增到java.lang.Object,而在Groovy中,方法的呼叫可以省略簽名中的括號,以一個空格分開即可,所以就有了上面的寫法。還有一點要說明的就是,在Groovy中,單引號和雙引號所包含的內容都是字串;不像Java中,單引號是字元,雙引號才是字串。

1.3 Gradle Wrapper

Wrapper,顧名思義,其實就是對Gradle的一層包裝,便於在團隊開發過程中統一Gradle構建的版本,這樣大家都可以使用統一的Gradle版本進行構建,避免因為Gradle版本不統一帶來的不必要的問題。

在這裡特別介紹的目的是因為,我們在專案開發過程中,用的都是Wrapper這種方式,而不是我們在1.1節裡介紹的自己下載ZIP壓縮包,配置Gradle的環境的方式。Wrapper在Windows下是一個批處理指令碼,在Linux下是一個shell指令碼。當你使用Wrapper啟動Gradle的時候,Wrapper會檢查Gradle有沒有被下載關聯,如果沒有將會從配置的地址(一般是Gradle官方庫)進行下載並執行構建。這對我們每個開發人員是非常方便的,因為你不用去專門配置環境了,只要執行Wrapper命令,它會幫你搞定一切。這種方式也方便我們在伺服器上做持續整合(CI),因為我們不用在伺服器上配置Gradle環境。

1.3.1 生成Wrapper

Gradle提供了內建的Wrapper task幫助我們自動生成Wrapper所需的目錄檔案,在一個專案的根目錄中輸入gradle wrapper即可生成:

$ gradle wrapper
:wrapper

BUILD SUCCESSFUL

Total time: 2.804 secs

This build could be faster, please consider using the Gradle Daemon: gradle.org/docs/2.14.1…複製程式碼

生成的檔案如下:

├──gradle
│ └──wrapper
│ ├──gradle-wrapper.jar
│ └──gradle-wrapper.properties
├──gradlew
└──gradlew.bat複製程式碼

gradlew和gradlew.bat分別是Linux和Windows下的可執行指令碼,它們的用法和Gradle原生命令是一樣的,Gradle怎麼用,它們也就可以怎麼用。gradle-wrapper.jar是具體業務邏輯實現的jar包,gradlew最終還是使用Java執行的這個jar包來執行相關Gradle操作。gradle-wrapper.properties是配置檔案,用於配置使用哪個版本的Gradle等,稍後會詳細講解。

這些生成的Wrapper檔案可以作為你專案工程的一部分提交到程式碼版本控制系統裡(Git),這樣其他開發人員就會使用這裡配置好的、統一的Gradle進行構建開發。

1.3.2 Wrapper配置

當我們在終端執行gradle wrapper生成相關檔案的時候,可以為其指定一些引數,來控制Wrapper的生成,比如依賴的版本等,如表1-1。

表1-1 Wrapper配置引數

引數名

說明

–gradle-version

用於指定使用的Gradle版本

–gradle-distribution-url

用於指定下載Gradle發行版的url地址

使用方法為gradle wrapper –gradle-version 2.4,這樣就意味著我們配置Wrapper使用2.4版本的Gradle,它會影響gradle-wrapper.properties中的distributionUrl的值,該值的規則是http://services.gradle.org/distributions/gradle-${gradleVersion}-bin.zip。

如果我們在呼叫gradle wrapper的時候不新增任何引數,那麼就會使用你當前Gradle的版本作為生成的Wrapper的gradle version。例如,你當前安裝的Gradle是2.8版本的,那麼生成的Wrapper也是2.8版本的。

1.3.3 gradle-wrapper.properties

該配置檔案是gradle wrapper的相關配置檔案,我們上面執行該任務的任何配置都會被寫進該檔案中。現在我們來看看該檔案的配置欄位,如表1-2。

表1-2 gradle-wrapper.properties的配置欄位

欄位名

說明

distributionBase

下載的Gradle壓縮包解壓後儲存的主目錄

distributionPath

相對於distributionBase的解壓後的Gradle壓縮包的路徑

zipStoreBase

同distributionBase,只不過是存放zip壓縮包的

zipStorePath

同distributionPath,只不過是存放zip壓縮包的

distributionUrl

Gradle發行版壓縮包的下載地址

我們比較關注的就是distributionUrl這個欄位,這個決定你的gradle wrapper依賴哪個Gradle版本。一般生成的都是這樣的https://services.gradle.org/distributions/gradle-2.14.1-bin.zip,我通常都會把bin改為all,這樣在開發過程中,就可以看到Gradle的原始碼了。

基於Gradle 2.14.1預設生成的gradle-wrapper.properties如下:

#Wed Sep 16 23:14:52 CST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https://services.gradle.org/distributions/gradle-2.14.1-bin.zip複製程式碼

distributionUrl是下載Gradle的路徑,如果執行./gradlew的時候計算機一直被卡著不動,可能是因為官方的Gradle地址被封閉了,建議把該地址換成別的映象地址。

1.3.4 自定義Wrapper Task

前面我們講了,gradle-wrapper.properties是由Wrapper Task生成的,那麼我們是否可以自定義配置該Wrapper task來達到我們配置gradle-wrapper.properties的目的呢?答案是肯定的。在build.gradle構建檔案中錄入如下指令碼:

task wrapper(type: Wrapper) {
gradleVersion = `2.4`
}複製程式碼

這樣我們再執行gradle wrapper的時候,就會預設生成2.4版本的wrapper了,而不用使用–gradle-version 2.4進行指定了。同樣,你也可以配置其他引數:

task wrapper(type: Wrapper) {
gradleVersion = `2.4`
archiveBase = `GRADLE_USER_HOME`
archivePath = `wrapper/dists`
distributionBase = `GRADLE_USER_HOME`
distributionPath = `wrapper/dists`
distributionUrl = `http://services.gradle.org/distributions/gradle-2.4-all.zip`
}複製程式碼

以上是我自己配置的一些值,也可以修改成你自己的。

1.4 Gradle日誌

在這裡單獨介紹Gradle日誌是為了便於我們在遇到問題的時候,能夠根據日誌資訊分析和解決問題。Gradle的日誌和Java、Android的差不多,也分一些級別,用於分類顯示日誌資訊,這樣我們只需根據不同的情況顯示不同類別的資訊,不至於被大量的日誌搞得暈頭轉向。

1.4.1 日誌級別

上面提到Gradle的日誌級別和我們使用的大部分語言的差不多。除了這些通用的之外,Gradle又增加了QUIET和LIFECYCLE兩個級別,用於標記重要以及進度級別的日誌資訊,如表1-3。

表1-3 日誌級別

級別

用於

ERROR

錯誤訊息

QUIET

重要訊息

WARNING

警告訊息

LIFECYCLE

進度訊息

INFO

資訊訊息

DEBUG

除錯資訊

表1-3明確列出了6種日誌級別以及它們的作用,現在我們就看一下怎樣使用它們。要使用它們,顯示我們想要顯示級別的日誌,就要通過命令列選項中的日誌開關來控制。

#輸出QUIET級別及其之上的日誌資訊
$ gradle -q tasks
#輸出INFO級別及其之上的日誌資訊
$ gradle -i tasks複製程式碼

以下列出所有通過命令列開關選項可以控制的級別,在命令列裡只需加上這些選項即可控制使用,如表1-4。

表1-4 日誌開關選項

開關選項

輸出的日誌級別

無選項

LIFECYCLE及其更高階別

-q或者 –quiet

QUIET及其更高階別

-i或者 –info

INFO及其更高階別

-d或者 –debug

DEBUG及其更高階別,這一般會輸出所有日誌

1.4.2 輸出錯誤堆疊資訊

在使用Gradle構建的時候,難免會有這樣或者那樣的問題導致你的構建失敗,這時就需要你根據日誌分析解決問題。除了以上的日誌資訊之外,Gradle還提供了堆疊資訊的列印,相信大家用過Java語言的都會很熟悉錯誤堆疊資訊,它能幫助我們很好地定位和分析問題。

預設情況下,堆疊資訊的輸出是關閉的,需要我們通過命令列的堆疊資訊開關開啟它,這樣在我們構建失敗的時候,Gradle才會輸出錯誤堆疊資訊,便於我們定位分析和解決問題,如表1-5。

表1-5 錯誤堆疊開關選項

命令列選項

用於

無選項

沒有堆疊資訊輸出

-s或者 –stacktrace

輸出關鍵性的堆疊資訊

-S或者–full-stacktrace

輸出全部堆疊資訊

一般推薦使用-s而不是-S,因為-S輸出的堆疊太多太長,非常不好看;而-s比較精簡,可以定位解決我們大部分的問題。

1.4.3 自己使用日誌資訊除錯

在編寫Gradle指令碼的過程中,我們有時候需要輸出一些日誌,來驗證我們的邏輯或者一些變數的值是否正確,這時候我們就可以使用Gradle提供的日誌功能。

通常情況下我們一般都是使用print系列方法,把日誌資訊輸出到標準的控制檯輸出流(它被Gradle定向為QUIET級別日誌):

println`輸出一段日誌資訊`複製程式碼

除了print系列方法之外,你也可以使用內建的logger更靈活地控制輸出不同級別的日誌資訊:

logger.quiet(`quiet日誌資訊.`)
logger.error(`error日誌資訊.`)
logger.warn(`warn日誌資訊.`)
logger.lifecycle(`lifecycle日誌資訊.`)
logger.info(`info日誌資訊.`)
logger.debug(`debug日誌資訊.`)複製程式碼

這裡其實是呼叫的Project的getLogger()方法獲取的Logger物件的例項。

1.5 Gradle命令列

Gradle命令列單獨用一節講解的目的是,想提倡大家儘可能使用命令列,而不要太依賴於各種IDE。雖然IDE很方便,但是,如果你換了一家公司,不使用這個IDE,如果讓你做自動構建沒有IDE可用,全部都是基於命令列的。這個就像我們第一次學習程式語言時老師沒說讓你用IDE,而是直接用記事本或者其他文字工具寫程式,目的就是讓我們不要太依賴第三方工具,這樣才能以不變應萬變。那麼IDE該不該用,有沒有必要,這個是肯定的,一定要用,因為它能提高工作效率。但是用之前你要知道如果不借助IDE做一件事,比如執行Gradle一個Task,在Android Studio下很簡單,雙擊那個Task就可以執行了,但是如果沒有Android Studio,你也要知道如何在命令列下執行它。我們要知其所以然,不然你的開發水平很難提高。

1.5.1 記得使用幫助

命令列下的工具都有命令。若剛開始我們不會用或者不知道有什麼命令或者引數,我們可以通過幫助來了解。基本上所有的命令列工具都有幫助,檢視幫助的方式也很簡單,基本上都是在命令後跟-h或者–help,有的時候會有-?,以Gradle Wrapper為例:

./gradlew -?
./gradlew -h
./gradlew –help複製程式碼

1.5.2 檢視所有可執行的Tasks

有時候我們不知道如何構建一個功能,不知道執行哪個Task,這時候就需要檢視哪些Task可執行,都具備什麼功能。通過執行./gradlew tasks命令,輸出如下:

:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
components - Displays the components produced by root project `flysnow`. [incubating]
dependencies - Displays all dependencies declared in root project `flysnow`.
dependencyInsight - Displays the insight into a specific dependency in root project`flysnow`.
help - Displays a help message.
model - Displays the configuration model of root project `flysnow`. [incubating]
projects - Displays the sub-projects of root project `flysnow`.
properties - Displays the properties of root project `flysnow`.
tasks - Displays the tasks runnable from root project `flysnow`.

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 2.321 secs

This build could be faster, please consider using the Gradle Daemon: gradle.org/docs/2.14.1…複製程式碼

從輸出中我們可以看到,Gradle會以分組的方式列出Task列表,比如構建類的有init、wrapper,幫助類的有help、tasks等。

1.5.3 Gradle Help任務

除了上面我們說的每個命令列都有幫助外,Gradle還內建了一個help task,這個help可以讓我們瞭解每一個Task的使用幫助,用法是 ./gradlew help –task。比如 ./gradlew help –task tasks,就可以顯示tasks任務的幫助資訊:

:help
Detailed task information for tasks

Path
:tasks

Type
TaskReportTask (org.gradle.api.tasks.diagnostics.TaskReportTask)

Options
--all Show additional tasks and detail.

Description
Displays the tasks runnable from root project `android-gradle-book-code` (some
of the displayed tasks may belong to subprojects).

Group
help

BUILD SUCCESSFUL複製程式碼

從幫助資訊中我們可以看到這個Task有什麼用,是什麼型別,屬於哪個分組,有哪些可以使用的引數。比如這裡就有–all引數,可以檢視很多額外的詳細資訊。

1.5.4 強制重新整理依賴

我們一個功能不可避免地會依賴很多第三方庫。像Maven這類工具都是有快取的,因為不可能每次編譯的時候都要重新下載第三方庫,快取就是這個目的,先使用快取,沒有再下載第三方庫。預設情況下Maven這類工具會控制快取的更新,但是也有例外,比如Version,裡面的程式碼變了,還有就是聯調測試時使用的snapshot版本。以上兩種情況我們在實際專案中都遇到過,最後就是通過強制重新整理解決的。強制重新整理很簡單,只要在命令列執行的時候加上–refresh-dependencies引數就可以,這是IDE很難做到的(需要你瞭解配置)。所以,命令列的優勢就體現出來了,非常簡單:

./gradlew --refresh-dependencies assemble複製程式碼

其他還有很多有用的命令、引數以及Tasks,就不一一介紹了,大家可以通過上面講的兩種幫助方法來了解。

1.5.5 多工呼叫

有時候我們需要同時執行多個任務,比如在執行jar之前先進行clean,那麼我們就需要先執行clean對class檔案清理,然後再執行jar生成一個jar包。通過命令列執行多個任務非常簡單,只需要按順序以空格分開即可,比如./gradlew clean jar,這樣就可以了。有更多的任務時,可以繼續新增。

1.5.6 通過任務名字縮寫執行

有的時候我們的任務名字很長,如果在執行的時候全部寫一遍也挺費時間,為此Gradle提供了基於駝峰命名法的縮寫呼叫,比如connectCheck,我們執行的時候可以使用./gradlew connectCheck,也可以使用./gradlew cc這樣的方式來執行。

相關文章