Gradle 完整指南(Android)

前言

為什么需要學(xué)Gradle?

Gradle 是 Android 現(xiàn)在主流的編譯工具绿聘,雖然在Gradle 出現(xiàn)之前和之后都有對(duì)應(yīng)更快的編譯工具出現(xiàn)趾浅,但是 Gradle 的優(yōu)勢(shì)就在于它是親兒子扛吞,Gradle 確實(shí)比較慢鹃觉,這和它的編譯過程有關(guān)厢钧,但是現(xiàn)在的Gradle 編譯速度已經(jīng)有了成倍提高鳞尔。除此之外,相對(duì)其他編譯工具早直,最重要的寥假,他和 Android Studio 的關(guān)系非常緊密,可以說對(duì)于一些簡(jiǎn)單的程序我們幾乎不需要任何代碼上的配置只使用 Android Studio 就可以完成編譯和運(yùn)行霞扬。

但是對(duì)于一些比較復(fù)雜的炸裆,特別是多人團(tuán)隊(duì)合作的項(xiàng)目我們會(huì)需要一些個(gè)性化的配置來提高我們的開發(fā)效率朝巫。比如我們要自定義編譯出的apk包的名字、對(duì)于一些特殊產(chǎn)品我們可能會(huì)要用同一個(gè)項(xiàng)目編譯出免費(fèi)版和付費(fèi)版的apk。這些高級(jí)的功能都需要我們對(duì)配置代碼進(jìn)行自定義地修改务热。

最近伴隨著 Android Studio2.0的發(fā)布, Gradle 也進(jìn)行了一次非常大的升級(jí)酒奶,叫Instant Run.它的編譯速度網(wǎng)上有人用逆天兩個(gè)字來形容关摇。當(dāng)我們第一次點(diǎn)擊run、debug按鈕的時(shí)候肆汹,它運(yùn)行時(shí)間和我們往常一樣愚墓。但是接下去的時(shí)間里,你每次修改代碼后點(diǎn)擊run昂勉、debug按鈕浪册,對(duì)應(yīng)的改變將迅速的部署到你正在運(yùn)行的程序上,傳說速度快到你都來不及把注意力集中到手機(jī)屏幕上硼啤,它就已經(jīng)做好相應(yīng)的更改议经。但是剛出來的似乎對(duì)一些項(xiàng)目的兼容性不太好,現(xiàn)在升級(jí)后不知道怎么樣谴返。

為什么要了解命令行編譯煞肾?

在很多情況下我們都是使用的 Android Studio 來build、debug項(xiàng)目嗓袱。Android Studio 能滿足我們開發(fā)的大多數(shù)需求籍救,但是某些情況下命令行能夠讓我們編譯的效率更高,過程更明朗渠抹,一些高級(jí)的配置也需要熟悉命令行才能夠使用蝙昙,比如在服務(wù)器編譯闪萄,某些項(xiàng)目初始化的時(shí)候如果直接交給Android Studio ,它會(huì)一直Loading,你都不知道它在干嘛奇颠,但是用命令行你就知道它卡在了哪個(gè)環(huán)節(jié)败去,你只需要修改某些代碼,馬上就能夠編譯過了烈拒。

了解 Gradle 之后我們可以做什么圆裕?

we can do everything what we want.

自定義編譯輸出文件格式。

hook Android 編譯過程荆几。

配置和改善 Gradle 編譯速度

Gralde Overview

History

我們知道吓妆,Android 的編譯過程非常復(fù)雜:

我們需要一種工具幫我們更快更方便更簡(jiǎn)潔地完成 Android 程序的編譯。現(xiàn)在結(jié)合Android Studio 我們一般使用的工具都是Gradle, 在 Gradle 出現(xiàn)以前Android 也有對(duì)應(yīng)的編譯工具叫Ant,在Gradle 出現(xiàn)之后吨铸,也有新的編譯工具出現(xiàn)行拢,就是FaceBook 的Buck工具。這些編譯工具在出現(xiàn)的時(shí)候幾乎都比 Gradle 要快诞吱,Gradle 之所以慢是跟它的編譯周期有很大關(guān)系舟奠。

Gradle 的編譯周期

在解析 Gradle 的編譯過程之前我們需要理解在 Gradle 中非常重要的兩個(gè)對(duì)象。ProjectTask狐胎。

每個(gè)項(xiàng)目的編譯至少有一個(gè) Project,一個(gè)build.gradle就代表一個(gè)project,每個(gè)project里面包含了多個(gè)task,task 里面又包含很多action鸭栖,action是一個(gè)代碼塊,里面包含了需要被執(zhí)行的代碼握巢。

在編譯過程中晕鹊, Gradle 會(huì)根據(jù)build 相關(guān)文件,聚合所有的project和task暴浦,執(zhí)行task 中的 action溅话。因?yàn)閎uild.gradle文件中的task非常多,先執(zhí)行哪個(gè)后執(zhí)行那個(gè)需要一種邏輯來保證歌焦。這種邏輯就是依賴邏輯飞几,幾乎所有的Task 都需要依賴其他 task 來執(zhí)行,沒有被依賴的task 會(huì)首先被執(zhí)行独撇。所以到最后所有的 Task 會(huì)構(gòu)成一個(gè)有向無環(huán)圖(DAG Directed Acyclic Graph)的數(shù)據(jù)結(jié)構(gòu)屑墨。

編譯過程分為三個(gè)階段:

初始化階段:創(chuàng)建 Project 對(duì)象,如果有多個(gè)build.gradle纷铣,也會(huì)創(chuàng)建多個(gè)project.

配置階段:在這個(gè)階段卵史,會(huì)執(zhí)行所有的編譯腳本,同時(shí)還會(huì)創(chuàng)建project的所有的task搜立,為后一個(gè)階段做準(zhǔn)備以躯。

執(zhí)行階段:在這個(gè)階段,gradle 會(huì)根據(jù)傳入的參數(shù)決定如何執(zhí)行這些task,真正action的執(zhí)行代碼就在這里.

剛剛我們提到Gradle 編譯的時(shí)候的一些相關(guān)文件,下面我們挨個(gè)解析一下這些文件忧设。

Gradle Files

對(duì)于一個(gè)gradle 項(xiàng)目刁标,最基礎(chǔ)的文件配置如下:

一個(gè)項(xiàng)目有一個(gè)setting.gradle、包括一個(gè)頂層的build.gradle文件址晕、每個(gè)Module 都有自己的一個(gè)build.gradle文件膀懈。

setting.gradle:這個(gè) setting 文件定義了哪些module 應(yīng)該被加入到編譯過程,對(duì)于單個(gè)module 的項(xiàng)目可以不用需要這個(gè)文件斩箫,但是對(duì)于 multimodule 的項(xiàng)目我們就需要這個(gè)文件吏砂,否則gradle 不知道要加載哪些項(xiàng)目。這個(gè)文件的代碼在初始化階段就會(huì)被執(zhí)行乘客。

頂層的build.gradle:頂層的build.gradle文件的配置最終會(huì)被應(yīng)用到所有項(xiàng)目中。它典型的配置如下:

buildscript {? ? repositories {? ? ? ? jcenter()? ? }? ? dependencies {? ? ? ? classpath'com.android.tools.build:gradle:1.2.3'}}allprojects{? ? repositories{? ? ? ? jcenter()? ? }}

buildscript:定義了 Android 編譯工具的類路徑淀歇。repositories中,jCenter是一個(gè)著名的 Maven 倉庫易核。

allprojects:中定義的屬性會(huì)被應(yīng)用到所有 moudle 中,但是為了保證每個(gè)項(xiàng)目的獨(dú)立性浪默,我們一般不會(huì)在這里面操作太多共有的東西牡直。

每個(gè)項(xiàng)目單獨(dú)的 build.gradle:針對(duì)每個(gè)moudle 的配置,如果這里的定義的選項(xiàng)和頂層build.gradle定義的相同纳决,后者會(huì)被覆蓋碰逸。典型的 配置內(nèi)容如下:

apply plugin:第一行代碼應(yīng)用了Android 程序的gradle插件,作為Android 的應(yīng)用程序阔加,這一步是必須的饵史,因?yàn)閜lugin中提供了Android 編譯、測(cè)試胜榔、打包等等的所有task胳喷。

android:這是編譯文件中最大的代碼塊,關(guān)于android 的所有特殊配置都在這里夭织,這就是又我們前面的聲明的 plugin 提供的吭露。

defaultConfig就是程序的默認(rèn)配置,注意尊惰,如果在AndroidMainfest.xml里面定義了與這里相同的屬性讲竿,會(huì)以這里的為主。

這里最有必要要說明的是applicationId的選項(xiàng):在我們?cè)?jīng)定義的AndroidManifest.xml中弄屡,那里定義的包名有兩個(gè)用途:一個(gè)是作為程序的唯一識(shí)別ID,防止在同一手機(jī)裝兩個(gè)一樣的程序题禀;另一個(gè)就是作為我們R資源類的包名。在以前我們修改這個(gè)ID會(huì)導(dǎo)致所有用引用R資源類的地方都要修改琢岩。但是現(xiàn)在我們?nèi)绻薷腶pplicationId只會(huì)修改當(dāng)前程序的ID,而不會(huì)去修改源碼中資源文件的引用投剥。

buildTypes:定義了編譯類型,針對(duì)每個(gè)類型我們可以有不同的編譯配置担孔,不同的編譯配置對(duì)應(yīng)的有不同的編譯命令江锨。默認(rèn)的有debug吃警、release 的類型。

dependencies:是屬于gradle 的依賴配置啄育。它定義了當(dāng)前項(xiàng)目需要依賴的其他庫酌心。

Gradle Wrapper

Gradle 不斷的在發(fā)展,新的版本難免會(huì)對(duì)以往的項(xiàng)目有一些向后兼容性的問題挑豌,這個(gè)時(shí)候,gradle wrapper就應(yīng)運(yùn)而生了安券。

gradlw wrapper 包含一些腳本文件和針對(duì)不同系統(tǒng)下面的運(yùn)行文件。wrapper 有版本區(qū)分氓英,但是并不需要你手動(dòng)去下載侯勉,當(dāng)你運(yùn)行腳本的時(shí)候,如果本地沒有會(huì)自動(dòng)下載對(duì)應(yīng)版本文件铝阐。

在不同操作系統(tǒng)下面執(zhí)行的腳本不同址貌,在 Mac 系統(tǒng)下執(zhí)行./gradlew ...,在windows 下執(zhí)行g(shù)radle.bat進(jìn)行編譯徘键。

如果你是直接從eclipse 中的項(xiàng)目轉(zhuǎn)換過來的练对,程序并不會(huì)自動(dòng)創(chuàng)建wrapper腳本,我們需要手動(dòng)創(chuàng)建吹害。在命令行輸入以下命令即可

gradlewrapper--gradle-version2.4

它會(huì)創(chuàng)建如下目錄結(jié)構(gòu):

wrapper 就是我們使用命令行編譯的開始螟凭。下面我們看看 wrapper 有什么樣的作用。

Gradle basics

Gradle 會(huì)根據(jù)build 文件的配置生成不同的task它呀,我們可以直接單獨(dú)執(zhí)行每一個(gè)task螺男。通過./gradlew tasks列出所有task。如果通過同時(shí)還想列出每個(gè)task 對(duì)應(yīng)依賴的其他task钟些,可以使用./gradlew tasks -all烟号。

其實(shí)每當(dāng)我們?cè)贏ndroid Studio點(diǎn)擊 build,rebuild,clean菜單的時(shí)候,執(zhí)行的就是一些gradle task.

Android tasks

有四個(gè)基本的 task, Android 繼承他們分別進(jìn)行了自己的實(shí)現(xiàn):

assemble:對(duì)所有的 buildType 生成 apk 包政恍。

clean:移除所有的編譯輸出文件汪拥,比如apk

check:執(zhí)行l(wèi)int檢測(cè)編譯。

build:同時(shí)執(zhí)行assemble和check命令

這些都是基本的命令篙耗,在實(shí)際項(xiàng)目中會(huì)根據(jù)不同的配置迫筑,會(huì)對(duì)這些task 設(shè)置不同的依賴。比如 默認(rèn)的 assmeble 會(huì)依賴 assembleDebug 和assembleRelease宗弯,如果直接執(zhí)行assmeble脯燃,最后會(huì)編譯debug,和release 的所有版本出來蒙保。如果我們只需要編譯debug 版本辕棚,我們可以運(yùn)行assembleDebug。

除此之外還有一些常用的新增的其他命令,比如 install命令逝嚎,會(huì)將編譯后的apk 安裝到連接的設(shè)備扁瓢。

我們運(yùn)行的許多命令除了會(huì)輸出到命令行,還會(huì)在build文件夾下生產(chǎn)一份運(yùn)行報(bào)告补君。比如check命令會(huì)生成lint-results.html.在build/outputs中引几。

Configuration

BuildConfig

這個(gè)類相信大家都不會(huì)陌生,我們最常用的用法就是通過BuildConfig.DEBUG來判斷當(dāng)前的版本是否是debug版本挽铁,如果是就會(huì)輸出一些只有在 debug 環(huán)境下才會(huì)執(zhí)行的操作伟桅。 這個(gè)類就是由gradle 根據(jù) 配置文件生成的。為什么gradle 可以直接生成一個(gè)Java 字節(jié)碼類叽掘,這就得益于我們的 gradle 的編寫語言是Groovy, Groovy 是一種 JVM 語言楣铁,JVM 語言的特征就是,雖然編寫的語法不一樣够掠,但是他們最終都會(huì)編程 JVM 字節(jié)碼文件民褂。同是JVM 語言的還有 Scala,Kotlin 等等。

這個(gè)功能非常強(qiáng)大疯潭,我們可以通過在這里設(shè)置一些key-value對(duì),這些key-value 對(duì)在不同編譯類型的 apk 下的值不同面殖,比如我們可以為debug 和release 兩種環(huán)境定義不同的服務(wù)器竖哩。比如:

除此之外,我們還可以為不同的編譯類型的設(shè)置不同的資源文件脊僚,比如:

Repositories

Repositories 就是代碼倉庫相叁,這個(gè)相信大家都知道,我們平時(shí)的添加的一些 dependency 就是從這里下載的辽幌,Gradle 支持三種類型的倉庫:Maven,Ivy和一些靜態(tài)文件或者文件夾增淹。在編譯的執(zhí)行階段,gradle 將會(huì)從倉庫中取出對(duì)應(yīng)需要的依賴文件乌企,當(dāng)然虑润,gradle 本地也會(huì)有自己的緩存,不會(huì)每次都去取這些依賴加酵。

gradle 支持多種 Maven 倉庫拳喻,一般我們就是用共有的jCenter就可以了。

有一些項(xiàng)目猪腕,可能是一些公司私有的倉庫中的冗澈,這時(shí)候我們需要手動(dòng)加入倉庫連接:

如果倉庫有密碼,也可以同時(shí)傳入用戶名和密碼

我們也可以使用相對(duì)路徑配置本地倉庫陋葡,我們可以通過配置項(xiàng)目中存在的靜態(tài)文件夾作為本地倉庫:

Dependencies

我們?cè)谝脦斓臅r(shí)候亚亲,每個(gè)庫名稱包含三個(gè)元素:組名:庫名稱:版本號(hào),如下:

如果我們要保證我們依賴的庫始終處于最新狀態(tài),我們可以通過添加通配符的方式,比如:

但是我們一般不要這么做捌归,這樣做除了每次編譯都要去做網(wǎng)絡(luò)請(qǐng)求查看是否有新版本導(dǎo)致編譯過慢外肛响,最大的弊病在于我們使用過的版本很很困難是測(cè)試版,性能得不到保證陨溅,所以终惑,在我們引用庫的時(shí)候一定要指名依賴版本。

Local dependencies

File dependencies

通過files()方法可以添加文件依賴门扇,如果有很多jar文件雹有,我們也可以通過fileTree()方法添加一個(gè)文件夾,除此之外臼寄,我們還可以通過通配符的方式添加霸奕,如下:

Native libraries

配置本地.so庫。在配置文件中做如下配置吉拳,然后在對(duì)應(yīng)位置建立文件夾质帅,加入對(duì)應(yīng)平臺(tái)的.so文件。

文件結(jié)構(gòu)如下:

Library projects

如果我們要寫一個(gè)library項(xiàng)目讓其他的項(xiàng)目引用留攒,我們的bubild.gradle的plugin 就不能是andrid plugin了煤惩,需要引用如下plugin

applyplugin:'com.android.library'

引用的時(shí)候在setting文件中include即可。

如果我們不方便直接引用項(xiàng)目炼邀,需要通過文件的形式引用魄揉,我們也可以將項(xiàng)目打包成aar文件,注意拭宁,這種情況下洛退,我們?cè)陧?xiàng)目下面新建arrs文件夾,并在build.gradle 文件中配置 倉庫:

當(dāng)需要引用里面的某個(gè)項(xiàng)目時(shí)杰标,通過如下方式引用:

Build Variants

在開發(fā)中我們可能會(huì)有這樣的需求:

我們需要在debug 和 release 兩種情況下配置不同的服務(wù)器地址兵怯;

當(dāng)打市場(chǎng)渠道包的時(shí)候,我們可能需要打免費(fèi)版腔剂、收費(fèi)版媒区,或者內(nèi)部版、外部版的程序桶蝎。

渠道首發(fā)包通常需要要求在歡迎頁添加渠道的logo驻仅。等等

為了讓市場(chǎng)版和debug版同時(shí)存在與一個(gè)手機(jī),我們需要編譯的時(shí)候自動(dòng)給debug版本不一樣的包名登渣。

這些需求都需要在編譯的時(shí)候動(dòng)態(tài)根據(jù)當(dāng)前的編譯類型輸出不同樣式的apk文件噪服。這時(shí)候就是我們的buildType大展身手的時(shí)候了。

Build Type

android 默認(rèn)的帶有Debug和Release兩種編譯類型胜茧。比如我們現(xiàn)在有一個(gè)新的statging的編譯類型

Source sets

每當(dāng)創(chuàng)建一個(gè)新的build type 的時(shí)候粘优,gradle 默認(rèn)都會(huì)創(chuàng)建一個(gè)新的source set仇味。我們可以建立與main文件夾同級(jí)的文件夾,根據(jù)編譯類型的不同我們可以選擇對(duì)某些源碼直接進(jìn)行替換雹顺。

除了代碼可以替換丹墨,我們的資源文件也可以替換

除此之外,不同編譯類型的項(xiàng)目嬉愧,我們的依賴都可以不同贩挣,比如,如果我需要在staging和debug兩個(gè)版本中使用不同的log框架没酣,我們這樣配置:

Product flavors

前面我們都是針對(duì)同一份源碼編譯同一個(gè)程序的不同類型王财,如果我們需要針對(duì)同一份源碼編譯不同的程序(包名也不同),比如 免費(fèi)版和收費(fèi)版裕便。我們就需要Product flavors绒净。

注意,Product flavorsBuild Type是不一樣的,而且他們的屬性也不一樣偿衰。所有的 product flavor 版本和defaultConfig 共享所有屬性挂疆!

像Build type 一樣,product flavor 也可以有自己的source set文件夾下翎。除此之外缤言,product flavor 和 build type 可以結(jié)合,他們的文件夾里面的文件優(yōu)先級(jí)甚至高于 單獨(dú)的built type 和product flavor 文件夾的優(yōu)先級(jí)视事。如果你想對(duì)于 blue類型的release 版本有不同的圖標(biāo)墨闲,我們可以建立一個(gè)文件夾叫blueRelease,注意郑口,這個(gè)順序不能錯(cuò),一定是 flavor+buildType 的形式盾鳞。

更復(fù)雜的情況下犬性,我們可能需要多個(gè)product 的維度進(jìn)行組合,比如我想要 color 和 price 兩個(gè)維度去構(gòu)建程序腾仅。這時(shí)候我們就需要使用flavorDimensions:

根據(jù)我們的配置乒裆,再次查看我們的task,發(fā)現(xiàn)多了這些task:

Resource merge priority

在Build Type中定義的資源優(yōu)先級(jí)最大,在Library 中定義的資源優(yōu)先級(jí)最低推励。

Signing configurations

如果我們打包市場(chǎng)版的時(shí)候鹤耍,我們需要輸入我們的keystore數(shù)據(jù)。如果是debug 版本验辞,系統(tǒng)默認(rèn)會(huì)幫我們配置這些信息稿黄。這些信息在gradle 中都配置在signingConfigs中。

配置之后我們需要在build type中直接使用

Optimize

Speeding up multimodule builds

可以通過以下方式加快gradle 的編譯:

開啟并行編譯:在項(xiàng)目根目錄下面的gradle.properties中設(shè)置

org.gradle.parallel=true

開啟編譯守護(hù)進(jìn)程:該進(jìn)程在第一次啟動(dòng)后回一直存在跌造,當(dāng)你進(jìn)行二次編譯的時(shí)候杆怕,可以重用該進(jìn)程族购。同樣是在gradle.properties中設(shè)置。

org.gradle.daemon=true

org.gradle.jvmargs=-Xms256m -Xmx1024m

Reducing apk file

在編譯的時(shí)候陵珍,我們可能會(huì)有很多資源并沒有用到寝杖,此時(shí)就可以通過shrinkResources來優(yōu)化我們的資源文件,除去那些不必要的資源互纯。

如果我們需要查看該命令幫我們減少了多少無用的資源瑟幕,我們也可以通過運(yùn)行shrinkReleaseResources命令來查看log.

某些情況下,一些資源是需要通過動(dòng)態(tài)加載的方式載入的留潦,這時(shí)候我也需要像 Progard 一樣對(duì)我們的資源進(jìn)行keep操作只盹。方法就是在res/raw/下建立一個(gè)keep.xml文件,通過如下方式 keep 資源:

Manual shrinking

對(duì)一些特殊的文件或者文件夾愤兵,比如 國際化的資源文件鹿霸、屏幕適配資源,如果我們已經(jīng)確定了某種型號(hào)秆乳,而不需要重新適配懦鼠,我們可以直接去掉不可能會(huì)被適配的資源。這在為廠商適配機(jī)型定制app的時(shí)候是很用的屹堰。做法如下:

比如我們可能有非常多的國際化的資源肛冶,如果我們應(yīng)用場(chǎng)景只用到了English,Danish,Dutch的資源,我們可以直接指定我們的resConfig:

對(duì)于尺寸文件我們也可以這樣做

Profiling

當(dāng)我們執(zhí)行所有task的時(shí)候我們都可以通過添加--profile參數(shù)生成一份執(zhí)行報(bào)告在reports/profile中扯键。示例如下:

我們可以通過這份報(bào)告看出哪個(gè)項(xiàng)目耗費(fèi)的時(shí)間最多睦袖,哪個(gè)環(huán)節(jié)耗費(fèi)的時(shí)間最多。

Practice

在開發(fā)的過程中荣刑,我們可能會(huì)遇到很多情況需要我們能夠自己定義task馅笙,在自定義task 之前,我們先簡(jiǎn)單看看groovy 的語法厉亏。

Groovy

我們前面看到的那些build.gradle 配置文件董习,和xml 等的配置文件不同,這些文件可以說就是可以執(zhí)行的代碼爱只,只是他們的結(jié)構(gòu)看起來通俗易懂皿淋,和配置文件沒什么兩樣,這也是Google 之所以選擇Groovy 的原因恬试。除此之外窝趣,Groovy 是一門JVM 語言,也就是训柴,Groovy 的代碼最終也會(huì)被編譯成JVM 字節(jié)碼哑舒,交給虛擬機(jī)去執(zhí)行,我們也可以直接反編譯這些字節(jié)碼文件畦粮。

我們這里簡(jiǎn)單地說一下 groovy 一些語法散址。

變量

在groovy 中乖阵,沒有固定的類型,變量可以通過def關(guān)鍵字引用预麸,比如:

defname='Andy'

我們通過單引號(hào)引用一串字符串的時(shí)候這個(gè)字符串只是單純的字符串瞪浸,但是如果使用雙引號(hào)引用,在字符串里面還支持插值操作吏祸,

defname='Andy'defgreeting="Hello, $name!"

方法

類似 python 一樣对蒲,通過def關(guān)鍵字定義一個(gè)方法。方法如果不指定返回值贡翘,默認(rèn)返回最后一行代碼的值蹈矮。

defsquare(defnum){? ? num * num}square4

Groovy 也是通過Groovy 定義一個(gè)類:

classMyGroovyClass{StringgreetingStringgetGreeting() {return'Hello!'}}

在Groovy 中,默認(rèn)所有的類和方法都是pulic的鸣驱,所有類的字段都是private的泛鸟;

和java一樣,我們通過new關(guān)鍵字得到類的實(shí)例踊东,使用def接受對(duì)象的引用:def instance = new MyGroovyClass()

而且在類中聲明的字段都默認(rèn)會(huì)生成對(duì)應(yīng)的setter,getter方法北滥。所以上面的代碼我們可以直接調(diào)用instance.setGreeting 'Hello, Groovy!',注意闸翅,groovy 的方法調(diào)用是可以沒有括號(hào)的再芋,而且也不需要分號(hào)結(jié)尾。除此之外坚冀,我們甚至也可以直接調(diào)用济赎;

我們可以直接通過instance.greeting這樣的方式拿到字段值,但其實(shí)這也會(huì)通過其get方法记某,而且不是直接拿到這個(gè)值司训。

map、collections

在 Groovy 中液南,定義一個(gè)列表是這樣的:

Listlist= [1,2,3,4,5]

遍歷一個(gè)列表是這樣的:

list.each() { element ->printlnelement}

定義一個(gè) map 是這樣的:

MappizzaPrices = [margherita:10, pepperoni:12]

獲取一個(gè)map 值是這樣的:

pizzaPrices.get('pepperoni')pizzaPrices['pepperoni']

閉包

在Groovy 中有一個(gè)閉包的概念豁遭。閉包可以理解為就是 Java 中的匿名內(nèi)部類。閉包支持類似lamda形式的語法調(diào)用贺拣。如下:

def square = {num->num*num}square8

如果只有一個(gè)參數(shù),我們甚至可以省略這個(gè)參數(shù)捂蕴,默認(rèn)使用it作為參數(shù)譬涡,最后代碼是這樣的:

Closuresquare = {it* it}square16

理解閉包的語法后,我們會(huì)發(fā)現(xiàn)啥辨,其實(shí)在我們之前的配置文件里涡匀,android,dependencies這些后面緊跟的代碼塊,都是一個(gè)閉包而已溉知。

Groovy in Gradle

了解完 groovy 的基本語法后陨瘩,我們來看看 gradle 里面的代碼就好理解多了腕够。

apply

applyplugin:'com.android.application'

這段代碼其實(shí)就是調(diào)用了project對(duì)象的apply方法,傳入了一個(gè)以plugin為key的map舌劳。完整寫出來就是這樣的:

project.apply([plugin:'com.android.application'])

實(shí)際調(diào)用的時(shí)候會(huì)傳入一個(gè)DependencyHandler的閉包,代碼如下:

Task

運(yùn)行該 task

./gradlew hello

注意:我們前面說過帚湘,gradle的生命周期分三步,初始化甚淡,配置和執(zhí)行大诸。上面的代碼在配置過程就已經(jīng)執(zhí)行了,所以贯卦,打印出的字符串發(fā)生在該任務(wù)執(zhí)行之前资柔,如果要在執(zhí)行階段才執(zhí)行任務(wù)中的代碼應(yīng)該如下設(shè)置:

添加Action:前面我們說過task 包含系列的action,當(dāng)task 被執(zhí)行的時(shí)候,所有的action 都會(huì)被依次執(zhí)行撵割。如果我們要加入自己的action,我們可以通過復(fù)寫doFirst()和doLast()方法贿堰。

打印出來是這樣的:

Task 依賴:前面我們也說過,task 之間的關(guān)系就是依賴關(guān)系啡彬,關(guān)于Task 的依賴有兩種外遇,must RunAfter和dependsOn诡渴。比如:

task task1 <<{ printfln=""'task1'=""}=""task=""task2=""<<{=""'task2'=""task2.mustRunAfter=""task1<=""code=""/>

task task1 <<{ printfln=""'task1'=""}=""task=""task2=""<<{=""'task2'=""task2.dependsOn=""task1<=""code=""/>

他們的區(qū)別是,運(yùn)行的的時(shí)候前者必須要都按順序加入gradlew task2 task1執(zhí)行才可以順利執(zhí)行,否則單獨(dú)執(zhí)行每個(gè)任務(wù)哮伟,后者只需要執(zhí)行g(shù)radlew task2即可同時(shí)執(zhí)行兩個(gè)任務(wù)。

Practice

我們可以通過兩個(gè)例子來實(shí)踐task鬼廓。

keystore 保護(hù)

這里直接將 store 的密碼明文寫在這里對(duì)于產(chǎn)品的安全性來說不太好尤慰,特別是如果該源碼開源,別人就可以用你的 id 去發(fā)布app荔泳。對(duì)于這種情況,我們需要構(gòu)建一個(gè)動(dòng)態(tài)加載任務(wù),在編譯release 源碼的時(shí)候從本地文件(未加入git)獲取keystore 信息,如下:

你還可以設(shè)置一個(gè)保險(xiǎn)措施昨登,萬一我們的沒有找到對(duì)應(yīng)的文件需要用戶從控制臺(tái)輸入密碼

最后設(shè)置最終值

然后設(shè)置release 任務(wù)依賴于我們剛剛設(shè)置的任務(wù)

通過 hook Android 編譯插件 重命名 apk

最后編譯出來的apk 名字類似app-debug-1.0.apk禽捆。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末琐凭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖思犁,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件江掩,死亡現(xiàn)場(chǎng)離奇詭異策泣,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)危队,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門簿盅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挪鹏,“玉大人步责,你說我怎么就攤上這事遂鹊。” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵倡缠,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我茎活,道長(zhǎng)昙沦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任妙色,我火速辦了婚禮桅滋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘身辨。我一直安慰自己丐谋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布煌珊。 她就那樣靜靜地躺著号俐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪定庵。 梳的紋絲不亂的頭發(fā)上吏饿,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音蔬浙,去河邊找鬼猪落。 笑死,一個(gè)胖子當(dāng)著我的面吹牛畴博,可吹牛的內(nèi)容都是我干的笨忌。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼俱病,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼官疲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起亮隙,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤途凫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后溢吻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體维费,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了犀盟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片噪漾。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖且蓬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情题翰,我是刑警寧澤恶阴,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站豹障,受9級(jí)特大地震影響冯事,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜血公,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一昵仅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧累魔,春花似錦摔笤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至梯投,卻和暖如春命辖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背分蓖。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工尔艇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人么鹤。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓终娃,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親午磁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尝抖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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