在 Android Studio 構(gòu)建的項目中,基于 Gradle 進(jìn)行項目的構(gòu)建肚医,同時使用 Android DSL 進(jìn)行 Android 項目的配置分尸,而 Gradle 是基于 Groovy 腳本語言進(jìn)行開發(fā)姜挺,所以熟練掌握 Android Studio 中的項目配置兼贸,就需要掌握以下知識點:
- Groovy 基礎(chǔ)
- Gradle DSL 語法
- Android DSL 語法
1. Groovy 基礎(chǔ)
Groovy 和 Java 一樣也是一門 JVM 語言段直,最終都會編譯成 .class 文件然后運行在 JVM 上,Groovy 語言類似腳本溶诞,語法簡單更靈活,所以在編寫項目腳本構(gòu)建上優(yōu)勢更加明顯决侈。
1.1 Groovy 環(huán)境配置
工欲善其事必先利其器螺垢,編碼之前環(huán)境先行。網(wǎng)上有很多配置 Groovy 開發(fā)的環(huán)境赖歌,針對 Android 程序員來說枉圃,使用我們的 Android Studio 就能滿足。
第一步:創(chuàng)建一個 Application 或 Library 工程
工程創(chuàng)建好后庐冯,除了 build.gradle 文件和 src/main 目錄文件夾保留外孽亲,其它的都可以刪除。
第二步:在 src/main 目錄下創(chuàng)建一個 groovy 文件夾
在創(chuàng)建的 Module 工程中的 build.gradle 文件中添加以下依賴:
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
repositories {
mavenCentral()
}
第三步:在 groovy 文件夾中創(chuàng)建 .groovy 文件
Android Studio 中會自動識別 groovy 文件夾下的 .groovy 文件展父。
第四步:配置運行環(huán)境
- 點擊 Edit Configurations
1.2 Groovy 基本語法
1.2.1 基本數(shù)據(jù)類型
Groovy 是弱化類型語言返劲,但是實現(xiàn)上還是強類型相關(guān),如果類型不對栖茉,還是會報錯篮绿。Groovy 中的基本數(shù)據(jù)類型有:
- byte -這是用來表示字節(jié)值。例如2吕漂。
- short -這是用來表示一個短整型亲配。例如10。
- int -這是用來表示整數(shù)惶凝。例如1234吼虎。
- long -這是用來表示一個長整型。例如10000090苍鲜。
- float -這是用來表示32位浮點數(shù)思灰。例如12.34。
- double -這是用來表示64位浮點數(shù)坡贺,這些數(shù)字是有時可能需要的更長的十進(jìn)制數(shù)表示官辈。例如12.3456565箱舞。
- char -這定義了單個字符文字。例如“A”拳亿。
- Boolean -這表示一個布爾值晴股,可以是true或false。
- String -這些是以字符串的形式表示的文本肺魁。例如电湘,“Hello World”的。
int a = 4;
float b = 1.0;
double c = 2.1;
byte te = 4;
char ch = 'c';
String str = "abcd";
1.2.2 變量和方法的聲明
在 Groovy 中通過 def 關(guān)鍵字進(jìn)行變量和方法的聲明鹅经。
def a = 1
def b = 1.0
def str = "Hello World"
def output() {
print 'Hello World'
}
Groovy 類似腳本寂呛,所以有很多都可以省略:
- 語句后的分號;可以省略
- 變量的類型可以省略
- 方法返回值 return 語句可以省略
方法的聲明
Groovy 也是一門 JVM 語言,所以在語法上與 Java 有很多相通的地方瘾晃,這樣在方法的聲明時候格式也比較隨意贷痪,所以作為 Android 程序員,我們可以選擇靠攏 Java 語法格式的風(fēng)格蹦误。
def methodName() {
print("HelloWorld")
}
def int sum2Number(a, b) {
return a + b
}
def sum(a, b) {
return a+b
}
int add(a ,b) {
return a + b
}
1.2.3 循環(huán)
Groovy 中循環(huán)控制語句與 Java 中的類似劫拢,有以下三種:
- for 語句
- while 語句
- for-in 語句
for(int i = 0 ;i < 9; i++) {
println(i)
}
int i = 0
while(i++ < 9) {
println(i)
}
for(int j in 1..9) {
println(j)
}
同樣,針對循環(huán)也有循環(huán)控制語句:
- break:break語句用于結(jié)束循環(huán)和switch語句內(nèi)的控制流强胰。
- continue:結(jié)束本次循環(huán)舱沧,進(jìn)行下次循環(huán),僅限于while和for循環(huán)偶洋。
for(int i = 0 ;i < 9; i++) {
println(i)
continue
}
for(int j in 1..9) {
println(j)
break
}
1.2.4 條件判斷語句
Groovy 中的條件判斷語句與 Java 中的類似熟吏,有:
- if
- if...else
- if...else if...else
- switch
例子就不演示了,語法跟 Java 相同玄窝。
在上面的 Groovy 基礎(chǔ)介紹中牵寺,形式上跟 Java 語言非常相似,沒有太大的變化哆料,針對 Java 缸剪、Android 程序員來說應(yīng)該非常容易上手。
1.3 Groovy 中的集合
Java 中集合主要有:List东亦、Map 衍生出來的類杏节,在 Groovy 中同樣存在集合,名稱跟 Java 中的相同典阵,只不過形式發(fā)生改變了奋渔,更加簡單容易操作。Groovy 中的集合:
- List:亦稱為列表壮啊,列表是用于存儲數(shù)據(jù)項集合的結(jié)構(gòu)嫉鲸。
- Map:亦稱為映射,映射(也稱為關(guān)聯(lián)數(shù)組歹啼,字典玄渗,表和散列)是對象引用的無序集合座菠。
1.3.1 List 列表
基本語法:List 列表使用[ ] 進(jìn)行聲明,并通過索引進(jìn)行區(qū)分藤树。
def listEmpty = [] //空列表
def list = [1,2,3,4,5] //整數(shù)值列表
def listInt = [1,[2,3],4,5] //列表嵌套列表
def listString = ["andoter","note"] //字符串列表
def listNone = ["andoter",1,4] //異構(gòu)對象列表
列表中的方法:
- boolean add(E e)
- void add(int index, E element)
- boolean addAll(Collection<? extends E> c)
- void clear()
- boolean contains(Object o)
- Iterator<E> iterator()
- Object[] toArray()
- int lastIndexOf(Object o)
- E set(int index, E element)
這些方法都跟 Java 中的類似浴滴,打開對應(yīng)的類型查看后,發(fā)現(xiàn)通過 def 聲明的列表竟然是 java.util.List 下面的岁钓。
def listEmpty = [] //空列表
def list = [1,2,3,4,5] //整數(shù)值列表
def listInt = [1,[2,3],4,5] //列表嵌套列表
def listString = ["andoter","note"] //字符串列表
def listNone = ["andoter",1,4] //異構(gòu)對象列表
listEmpty.add(1)
listEmpty << 6
println(listEmpty.size())
list.clear()
println(listInt.contains([2,3]))
println(listString.lastIndexOf("note"))
println(listNone.indexOf(1))
需要注意升略,在 groovyjarjarantlr.collections.List 包下同樣存在 List,所以使用的時候需要注意屡限。
關(guān)于列表 List 的遍歷品嚣,我們可以參照 Java 中的 Iterator 接口去遍歷,或者使用 Groovy 系統(tǒng)提供的 each 方法進(jìn)行遍歷钧大。在 Groovy 中提供 DefaultGroovyMethods 類翰撑,該類定義很多快捷使用方法:
- abs:取絕對值計算
- addAll(Collection)
- each:遍歷
- eachWithIndex:帶 index 的遍歷
- grep:符合條件的element會被提取出來,形成一個list
- every:所有的element都滿足條件才返回true拓型,否則返回false
- any:只要存在一個滿足條件的element就返回true额嘿,否則返回false
- join:用指定的字符連接collection中的element
- sort:根據(jù)指定條件進(jìn)行排序
- find:查找collection中滿足條件的‘第一個’element
- findAll:查找collection中滿足條件的‘所有’element
很多使用的方法,可參照源碼查看劣挫。
def listString = ["andoter","note"]
listString.each {
println(it)
}
listString.each {
value -> println(value)
}
1.3.2 Map 映射
Map集合中的元素由鍵值訪問。 Map中使用的鍵可以是任何類东帅。當(dāng)我們插入到Map集合中時压固,需要兩個值:鍵和值。
def mapEmpty = [ : ]
def mapString = ["name":"andoter","email" : "andoter0504@gmail.com"]
def mapInt = ["name" : 123, "age" : 26]
映射中的方法:
- void clear()
- boolean containsValue(Object value)
- Map.Entry<K, V> eldest()
- Set<Map.Entry<K,V>> entrySet()
- void forEach(BiConsumer<? super K, ? super V> action)
- V get(Object key)
- Set<K> keySet()
- Collection<V> values()
總體上方法與 Java 中的 Map 相同靠闭。
def mapEmpty = [ : ]
def mapString = ["name":"andoter", "email" : "andoter0504@gmail.com"]
def mapInt = ["name" : 123, "age" : 26]
mapEmpty.put("name","andoter")
mapEmpty.values()
mapString.get("name")
mapInt.containsValue("123")
mapString.each {key, value ->
if(key == null || key.length() == 0) {
println("Null Object")
}
if(key.equals("name")){
println(key + "=" + value)
}else{
println(key + ":" + value)
}
}
1.4 Groovy 中的 IO 操作
Java 提供了 java.io.* 一系列方法用于文件的操作帐我,這些方法在 Groovy 中也適用。Groovy 針對 Java 提供的方法做了增強處理愧膀,更方便使用拦键。
def file = new File("/Users/dengshiwei/WorkProject/GradlePlugin/groovydemo/src/main/groovy/TestGroovy.groovy")
if (file.exists()) {
file.eachLine {
line ->
println(line)
}
} else {
print("File not exist")
}
這里簡單的示例下,更多的內(nèi)容請參照官方 API 接口檩淋。
1.5 閉包
閉包作為 Groovy 中非常重要的特性芬为,它使得 Groovy 語言更加靈活,在 Gradle 項目構(gòu)建中蟀悦,更是在 DSL 中大量被使用媚朦,所以掌握閉包的使用對掌握 Android 項目構(gòu)建有非常重要的作用。
1.5.1 閉包的定義
閉包的定義格式:
{
parameters -> statements
}
從形式上來看與 Lambda 表達(dá)式非常類似日戈,所以熟悉 Lambda 表達(dá)式的同學(xué)上手閉包非常簡單询张。如果閉包沒有定義參數(shù),它隱含一個參數(shù) it,類似 Java 中的 this浙炼,假設(shè)你的閉包不需要接受參數(shù),但是還是會生成一個隱式參數(shù) it佩伤,只不過它的值為 null,也就是說拷姿,閉包至少包含一個參數(shù)。
無參數(shù)的閉包
def closure = {
println("No Parameters")
}
一個參數(shù)的閉包
def closureOneParameters = {
key -> println(key)
}
兩個參數(shù)的閉包
def closure2Parameter = {
key,value->
if (key == 1) {
key = key + 1
println(key + ":" + value)
} else if (key == 2)
println(key + ":" + value)
}
1.5.2 閉包的特性
閉包的引入讓 Groovy 語言更加簡單恋拷、方便,比如作為函數(shù)的最后一個參數(shù)钮糖,閉包可以單獨寫在函數(shù)梅掠,本小節(jié)中介紹一下閉包常見的使用形式。
閉包特性:
- 閉包可以訪問外部的變量店归,方法是不能訪問外部變量的阎抒。
- 閉包中可以包含代碼邏輯,閉包中最后一行語句消痛,表示該閉包的返回值且叁,不論該語句是否冠名return關(guān)鍵字,如果最后一行語句沒有不輸入任何類型秩伞,閉包將返回null逞带。
- 閉包的參數(shù)聲明寫在‘->’符號前,調(diào)用閉包的的標(biāo)準(zhǔn)寫法是:閉包名.call(閉包參數(shù))纱新。
- 閉包的一些快捷寫法展氓,當(dāng)閉包作為閉包或方法的最后一個參數(shù)×嘲可以將閉包從參數(shù)圓括號中提取出來接在最后遇汞,如果閉包是唯一的一個參數(shù),則閉包或方法參數(shù)所在的圓括號也可以省略簿废。對于有多個閉包參數(shù)的空入,只要是在參數(shù)聲明最后的,均可以按上述方式省略族檬。
閉包作為函數(shù)參數(shù)
閉包作為函數(shù)參數(shù)時歪赢,跟普通的變量參數(shù)使用方式相同。
def checkKey = {
map ->
if (map.size() == 0) {
println("Parametes is Null or Empty")
}
println(map)
}
def enqueue(key, value, closure) {
def map = [:]
map.put(key, value)
closure(map)
}
enqueue(1, 2, checkKey)
通常情況下单料,在函數(shù)具有閉包作為參數(shù)的時候埋凯,會將閉包放在最后一個參數(shù)的位置,當(dāng)閉包作為最后一個參數(shù)的時候看尼,閉包可以抽離到函數(shù)體之外递鹉,提高函數(shù)的簡潔性。
關(guān)于 Groovy 比較好的文章
2. Gradle DSL 語言
上面我們針對 Groovy 語言進(jìn)行簡單的學(xué)習(xí)躏结,接下來就是 Gradle DSL 語言的學(xué)習(xí)。Gradle 是 Android Studio 中采用的全新項目構(gòu)建方式狰域。
Gradle 是一個開源的自動化構(gòu)建工具媳拴,提供更高的靈活和體驗黄橘。Gradle 腳本采用 Groovy 或 Kotlin 進(jìn)行編寫。官方文檔
2.1 基本概念
Gradle 是一種腳本配置屈溉,所以當(dāng)它執(zhí)行的時候塞关,它需要跟對應(yīng)的類型相對應(yīng)。在 Gradle 中存在以下三種類型:
腳本類型 | 委托的實例 |
---|---|
Build script | Project |
Init script | Gradle |
Settings script | Settings |
Gradle 圍繞項目 Project子巾,所以 Project 是我們最重要的接口,通過 Project 接口帆赢,我們可以獲取整個 Gradle 的屬性。通常我們的項目在 Project 模式的下結(jié)構(gòu)是:
├── app #Android App目錄
│ ├── app.iml
│ ├── build #構(gòu)建輸出目錄
│ ├── build.gradle #構(gòu)建腳本
│ ├── libs #so相關(guān)庫
│ ├── proguard-rules.pro #proguard混淆配置
│ └── src #源代碼线梗,資源等
├── build
│ └── intermediates
├── build.gradle #工程構(gòu)建文件
├── gradle
│ └── wrapper
├── gradle.properties #gradle的配置
├── gradlew #gradle wrapper linux shell腳本
├── gradlew.bat
├── LibSqlite.iml
├── local.properties #配置Androod SDK位置文件
└── settings.gradle #工程配置
2.2 Project
2.2.1 生命周期 Lifecycle
Project 與 build.gradle 文件是一對一的關(guān)系椰于,在初始化腳本構(gòu)建的過程中,Gradle 為每一個項目創(chuàng)建 Project對象仪搔。步驟如下:
初始化階段
在初始化階段瘾婿,構(gòu)建工具根據(jù)每個 build.gradle 文件創(chuàng)建出每個項目對應(yīng)的 Project,同時會執(zhí)行項目根目錄下的 settings.gradle 分析需要參與編譯的項目烤咧。
比如我們常見的 settings.gradle 配置文件:
include ':app', ':groovydemo'
指明了需要編譯的項目偏陪。
配置階段
配置階段為每個 Project 創(chuàng)建并配置 Task,配置階段會去加載所有參與構(gòu)建項目的 build.gradle 文件煮嫌,將每個 build.gradle 文件轉(zhuǎn)換為一個 Gradle 的 Project 對象笛谦,分析依賴關(guān)系,下載依賴昌阿。
執(zhí)行階段
Gradle 根據(jù) Task 之間的依賴關(guān)系揪罕,決定哪些 Task 需要執(zhí)行,以及 Task 之間的先后順序宝泵。
2.2.2 Task
Task 是 Gradle 中的最小執(zhí)行單元,所有的構(gòu)建轩娶、編譯儿奶、打包、debug鳄抒、test 等都是執(zhí)行了某一個 task闯捎,一個 Project 可以有多個 Task,Task 之間可以互相依賴许溅。例如我有兩個 Task瓤鼻,TaskA 和 TaskB,指定 TaskA 依賴 TaskB贤重,然后執(zhí)行 TaskA茬祷,這時會先去執(zhí)行 TaskB,TaskB 執(zhí)行完畢后在執(zhí)行 TaskA并蝗。
同時祭犯,我們也可以自定義 Task秸妥,也可以查找 Task 是否存在。站在編程的角度來看 Task 同樣是一個類沃粗,核心方法:
- String getName():獲取任務(wù)名稱
- Project getProject():獲取任務(wù)所在的 Project 對象
- List<Action<? super Task>> getActions():獲取 Action
- TaskDependency getTaskDependencies():獲取任務(wù)依賴
- Task dependsOn(Object... var1):任務(wù)依賴關(guān)系函數(shù)
- void onlyIf(Closure var1)
- TaskState getState():獲取任務(wù)的狀態(tài)
- Task doFirst(Closure var1):任務(wù)先執(zhí)行..
- Task doLast(Closure var1):任務(wù)后執(zhí)行..
- String getDescription():獲取任務(wù)描述
- String getGroup():獲取任務(wù)分組
1. 任務(wù)的創(chuàng)建
Task 是 Gradle 中最小執(zhí)行基本單元粥惧,創(chuàng)建任務(wù)的方式有以下幾種:
- Project.task(String name)
- Project.task(String name, Closure configureClosure)
- Project.task(Map<String, ?> args, String name, Closure configureClosure)
- Project.task(Map<String, ?> args, String name)
- TaskContainer.create(String name)
- TaskContainer.create(Map<String,?> options)
前面三種都是基于 Project 提供的 task 重載方法進(jìn)行創(chuàng)建。這里需要著重介紹下里面的 Map 參數(shù)最盅,Map 參數(shù)選項用于控制 Task 的創(chuàng)建以及屬性突雪。
配置項 | 描述 | 默認(rèn)值 |
---|---|---|
type | 任務(wù)創(chuàng)建的類型 | DefaultTask |
overwrite | 是否重寫已存在的任務(wù) | false |
dependsOn | 添加任務(wù)的依賴 | [] |
action | 添加任務(wù)中的 Action | null |
description | 任務(wù)的描述 | null |
group | 配置任務(wù)的分組 | null |
第一種:直接以任務(wù)名稱創(chuàng)建任務(wù)
Task copyTask = task("copyTask")
copyTask.description = "Copy Task"
copyTask.group = "custom"
copyTask.doLast {
print("Copy Task Create")
}
這種方式跟 Java 中的創(chuàng)建對象的方式非常相似,這種方式的本質(zhì)是調(diào)用 Project 類中的 task(String name) 方法進(jìn)行對象的創(chuàng)建涡贱。
第二種:task + 閉包的方式
Task taskClosure = project.task("taskClosure"){
print("Task Closure")
}
這種寫法利用閉包是最后一個參數(shù)的時候咏删,可以抽取到外部寫。上面的這種寫法也可以精簡:
task taskClousre {
print("Task Closure")
}
這種形式的在 .gradle 腳本文件中用的非常多盼产,所以大家也寫這種吧饵婆!
第三種:task + Map
Task mapTask = project.task(dependsOn: copyTask,description: "mapTask",group: "mapTask",
"mapTask"){
println("Map Task Create")
}
這里通過 Map 進(jìn)行 Task 的一些設(shè)置,這里我們可以同樣以方式一一樣戏售,單獨進(jìn)行設(shè)置侨核。
第四種:TaskContainer 創(chuàng)建任務(wù)
project.tasks.create("TaskContainer") {
description "TaskContainer"
group "TaskContainer"
doLast {
println("TaskContainer")
}
}
在上面的演示例子中,我們也介紹了任務(wù)的分組和描述的使用灌灾,可以在 Gradle Pojects 欄中進(jìn)行查看任務(wù)的分組和描述搓译。
2. 任務(wù)之間的關(guān)系
-
dependsOn(Task task) 任務(wù)依賴,通過 dependsOn 可以建立任務(wù)之間的執(zhí)行依賴關(guān)系,先執(zhí)行依賴的任務(wù)锋喜。
def name = "Hello World from" task checkName { if (name.length() > 0){ name = name.replace("from", "") } } task printName() { println(name) } printName.dependsOn(checkName)
-
mustRunAfter(Task task) 必須在添加的任務(wù)之后執(zhí)行些己。
def name = "Hello World from" task checkName { if (name.length() > 0){ name = name.concat(" China") } } task printName() { println(name) } printName.mustRunAfter(checkName)
3 任務(wù)類型
在 1 節(jié)中,我們提到了創(chuàng)建任務(wù)時可以通過 Map 配置任務(wù)的依賴屬性關(guān)系嘿般,里面涉及到 任務(wù)類型(type)段标,默認(rèn)值是 DefaultTask 類型,關(guān)于 type炉奴,我的理解是 Groovy 系統(tǒng)的 Task 類型逼庞,我們查看官方文檔,可以看到有很多 Task 類的子類瞻赶,這些應(yīng)該都可以作為 type 值進(jìn)行設(shè)置赛糟?那么常見的任務(wù)類型有哪些呢?
- Copy 類型 Task砸逊,Copy 任務(wù)的方法:
- eachFile:遍歷文件
- exclude(Closure excludeSpec):去除包含的內(nèi)容
- filter(Closure closure):過濾
- from(Object... sourcePaths):源目錄
- include(Closure includeSpec):包含內(nèi)容
- into(Object destDir):目標(biāo)目錄
- rename(Closure closure):重命名
- with(CopySpec... sourceSpecs)
通過 Copy 任務(wù)璧南,我們可以更方面實現(xiàn)文件的拷貝復(fù)制類操作,因為它提供了一些封裝好的方法师逸,不需要我們在通過 File 的操作進(jìn)行司倚。常見的就是創(chuàng)建的 makeJar 任務(wù),拷貝系統(tǒng)編譯的 jar 包到指定目錄。
task makeJar(type: Copy) {
def jarName = 'SensorsAnalytics-Android-SDK-Release-' + version + '.jar';
delete 'build/libs/' + jarName
from('build/intermediates/bundles/release/')
into('build/libs/')
include('classes.jar')
rename('classes.jar', jarName)
}
makeJar 的任務(wù)執(zhí)行步驟:首先設(shè)置 type 是 Copy 類型任務(wù),定義拷貝目標(biāo)的 jar 名稱对湃,接著刪除 目標(biāo)目錄已存在的 jar 文件崖叫,from 從源目錄拷貝到 into 的目標(biāo)目錄,包含 include 的 classes.jar拍柒,最后給文件重命名心傀。
在 Android Studio 的 2.X 版本中自動生成的 jar 包路徑在:build/intermediates/bundles/release/ 目錄,在 3.X 中目錄:build/intermediates/packaged-classes/release/拆讯。
- Jar 類型 Task
task makeJar(type: Jar, dependsOn: build) {
delete 'build/libs/sensorsdata.jar'
from('build/intermediates/classes/release')
into('build/libs/')
exclude('android/', 'androidx/','BuildConfig.class', 'R.class')
exclude {
it.name.startsWith('R$')
}
}
Jar 任務(wù)的作用就是打包 jar 包脂男,比如我們通過 from 指定工程目錄中的源碼進(jìn)行打包,這樣我們就可以實現(xiàn)高度的定制化种呐,不像通過 Copy 任務(wù)復(fù)制系統(tǒng)根據(jù)整個目錄生成的 Jar 包一樣宰翅。比如下面的 task 生成 Jar 包。
task makeJars(type: org.gradle.jvm.tasks.Jar) {
from(android.sourceSets.main.java.srcDirs)
}
在 Gradle 中提供了很多常見的任務(wù):
2.3. 常見腳本塊
Gradle 支持 Groovy 語言進(jìn)行編寫爽室,非常靈活支持各種插件汁讼。比如你想在腳本中使用一些第三方的插件、類庫等阔墩,就需要自己手動添加對這些插件嘿架、類庫的引用,而這些插件啸箫、類庫又不是直接服務(wù)于項目的耸彪,而是支持其它 build 腳本的運行,所以你應(yīng)當(dāng)將這部分的引用放置在 buildscript 代碼塊中忘苛。 gradle 在執(zhí)行腳本時蝉娜,會優(yōu)先執(zhí)行buildscript代碼塊中的內(nèi)容,然后才會執(zhí)行剩余的build腳本扎唾。所以需要我們了解常見的腳本塊配置召川。
2.3.1 allprojects { }
配置整個 Project 和子項目的配置。
allprojects {
repositories {
google()
jcenter()
}
}
比如我們在 allprojects 內(nèi)部定義的 Task 任務(wù)胸遇,就會用于根目錄和子項目扮宠。比如下面的例子,我們執(zhí)行:./gradlew print 任務(wù)狐榔,打印的結(jié)果如下:
allprojects {
task print {
println project.name
}
}
輸出結(jié)果:
GradlePlugin(根目錄)
app
groovydemo
sensorsdatalibrary
2.3.2 buildscript { }
buildscript 中的聲明是 gradle 腳本自身需要使用的資源。
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
//格式為-->group:module:version
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.2.4'
}
}
2.3.3 configurations { }
配置整個 Project 的 dependency 屬性获雕,與之對應(yīng)的是 ConfigurationContainer薄腻,在 Project 項目中可以通過以下方法獲取 ConfigurationContainer 對象:
- Project.getConfigurations()
- configurations
通常使用最多的就是去除依賴,比如我們添加的依賴中也依賴某個庫届案,這種間接依賴的沖突庵楷,transitive dependencies 被稱為依賴的依賴,
稱為“間接依賴”比較合適。
configurations {
compile.exclude module: 'commons'
all*.exclude group: 'org.gradle.test.excludes', module: 'reports'
}
2.3.4 dependencies { }
配置項目的依賴庫尽纽,與之對應(yīng)的是 DependencyHandler 類咐蚯。
在 dependencies{} 腳本塊中有不同的依賴方式,這里在 Android Studio 的 2.X 版本與 3.X 版本中差別還是挺大的弄贿,Android Studio3.0 中春锋,compile 依賴關(guān)系已被棄用,被 implementation 和 api 替代差凹,provided 被 compile only 替代期奔,apk 被 runtime only 替代。為了比較方便危尿,前面寫是 3.X 版本呐萌,括號是 2.X。
- implementation:依賴的庫只能在本項目使用谊娇,外部無法使用肺孤。比如我在一個 libiary 中使用 implementation 依賴了 gson 庫,然后我的主項目依賴了 libiary济欢,那么赠堵,我的主項目就無法訪問 gson 庫中的方法。這樣的好處是編譯速度會加快船逮,推薦使用 implementation 的方式去依賴顾腊,如果你需要提供給外部訪問,那么就使用 api 依賴即可
- api(compile):使用該方式依賴的庫將會參與編譯和打包
- testImplementation(testCompile):只在單元測試代碼的編譯以及最終打包測試 Apk 時有效
- debugImplementation(debugCompile):只在 debug 模式的編譯和最終的 debug Apk 打包時有效
- releaseImplementation(releaseCompile):僅僅針對 Release 模式的編譯和最終的 Release Apk 打包
- compileOnly(provided):只在編譯時有效挖胃,不會參與打包杂靶,可以在自己的moudle中使用該方式依賴。比如 com.android.support酱鸭,gson 這些使用者常用的庫吗垮,避免沖突。
- runtimeOnly(apk):只在生成 Apk 的時候參與打包凹髓,編譯時不會參與烁登,很少用。
下面是一些常見的依賴使用方式:
apply plugin: 'java'
//so that we can use 'compile', 'testCompile' for dependencies
dependencies {
//for dependencies found in artifact repositories you can use
//the group:name:version notation
compile 'commons-lang:commons-lang:2.6'
testCompile 'org.mockito:mockito:1.9.0-rc1'
//map-style notation:
compile group: 'com.google.code.guice', name: 'guice', version: '1.0'
//declaring arbitrary files as dependencies
compile files('hibernate.jar', 'libs/spring.jar')
//putting all jars from 'libs' onto compile classpath
compile fileTree('libs')
}
在實際項目開發(fā)中蔚舀,我們會引入很多第三方開源庫饵沧,自然就會造成依賴沖突,這里就涉及到在 dependencies 提供的配置字段:
- force = true:即使在有依賴庫版本沖突的情況下堅持使用被標(biāo)注的這個依賴庫版本
- transitive = true:依賴的依賴是否可用赌躺,舉個例子狼牺,使用的三方庫中可能也依賴別的庫,我們稱之為“間接依賴”
- exclude:用于排除指定版本庫礼患,通常用于排除沖突依賴庫
dependencies {
compile('com.sensorsdata.analytics.android:SensorsAnalyticsSDK:2.0.2') {
//強制使用我們依賴的 2.0.2 版本庫
force = true
//剔除間接依賴的庫,可以通過這三種方式是钥,后面再講解自定義插件的時候就能看懂這三種方式了掠归。
exclude module: 'cglib' //by artifact name
exclude group: 'org.jmock' //by group
exclude group: 'org.unwanted', module: 'iAmBuggy' //by both name and group
//禁用所有的間接依賴庫
transitive = false
}
}
2.3.5 repositories { }
配置 Project 項目所需的倉庫地址,Gradle 必須知道從哪里下載外部依賴悄泥,這是由倉庫配置來指定的虏冻,比如 google()、jcenter() 或 mavenCentral()弹囚。通常在 buildscript 腳本塊中也能看到配置的 repositories 屬性厨相,buildscript 中的聲明是 gradle 腳本自身需要使用的資源,可以聲明的資源包括依賴項余寥、 第三方插件领铐、 maven 倉庫地址等。而在 build.gradle 文件中直接聲明的依賴項宋舷、倉庫地址等信息是項目自身需要的資源绪撵。
repositories {
//Maven本地倉庫,尋找本地倉庫的邏輯與Maven相同
mavenLocal()
//Maven中心倉庫
mavenCentral()
//JCenter倉庫
jcenter()
//其它Maven遠(yuǎn)程倉庫
maven {
//可以指定身份驗證信息
credentials {
username 'user'
password 'password'
}
url "http://repo.mycompany.com/maven2"
//如果上面的URL找不到構(gòu)件祝蝠,則在下面找
artifactUrls "http://repo.mycompany.com/jars"
}
//Ivy遠(yuǎn)程倉庫
ivy {
url "http://repo.mycompany.com/repo"
}
//Ivy本地倉庫
ivy {
url "../local-repo"
}
//扁平布局的文件系統(tǒng)倉庫
flatDir {
dirs 'lib'
}
flatDir {
dirs 'lib1', 'lib2'
}
}
2.3.6 sourceSets { }
配置項目的源碼目錄結(jié)構(gòu)音诈。
sourceSets {
main {
java {
srcDirs = ['src/java']
}
resources {
srcDirs = ['src/resources']
}
}
}
2.3.7 subprojects { }
用于配置子項目的腳本塊。比如我們在 subprojects 中配置 print 任務(wù)绎狭,則只會作用于子目錄细溅。
subprojects {
task print {
println project.name
}
}
輸出結(jié)果:
app
groovydemo
sensorsdatalibrary
2.3.8 publishing { }
用于發(fā)布構(gòu)建。
publishing {
publications {
myPublication(MavenPublication) {
from components.java
artifact sourceJar
pom {
name = "Demo"
description = "A demonstration of Maven POM customization"
url = "http://www.example.com/project"
licenses {
license {
name = "The Apache License, Version 2.0"
url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
}
}
developers {
developer {
id = "johnd"
name = "John Doe"
email = "john.doe@example.com"
}
}
scm {
connection = "scm:svn:http://subversion.example.com/svn/project/trunk/"
developerConnection = "scm:svn:https://subversion.example.com/svn/project/trunk/"
url = "http://subversion.example.com/svn/project/trunk/"
}
}
}
}
}
2.4 Gradle 插件
通過使用插件可以擴(kuò)展項目的功能儡嘶,幫助我們做很多任務(wù)喇聊,比如編譯、打包蹦狂,Gradle 插件可以分為兩類:
- 二進(jìn)制插件:繼承 org.gradle.api.Plugin 接口實現(xiàn)的插件
- 腳本插件:直接在 build.gradle 配置文件
2.4.1 二進(jìn)制插件
常見的二進(jìn)制插件 com.android.application誓篱,這里的 ‘com.android.application’ 就是插件的 plugin id,二進(jìn)制插件的使用形式:
apply plugin: plugin id
apply plugin : 'java'
2.4.2 腳本插件
通常腳本插件用于本地的配置存儲凯楔,使用格式:
apply from: ‘fileName’
// config.gradle
rootProject.ext {
android = [
compileSdkVersion : 28,
buildToolsVersion : "28.0.0",
applicationId : "sw.andoter.com.gradleplugindemo",
minSdkVersion : 18,
targetSdkVersion : 28,
versionCode : 1,
versionName : "1.0"
]
sdkVersion = 13
apkPath = [
apkPath : "/Users/dengshiwei/Desktop/*.apk"
]
}
apply from: "../config.gradle"
2.5 Gradle 自定義插件
在基于 Gradle 的項目構(gòu)建中插件的使用非常常見窜骄,比如 com.android.application、com.android.library 等摆屯,如何自定義自己的插件呢邻遏?在 Gradle 中提供了 Plugin 接口用于自定義插件,本小節(jié)只做簡單介紹自定義插件的步驟:
創(chuàng)建一個 module虐骑,什么樣的都可以准验,不管是 Phone&Tablet Module 或 Android Librarty 都可以,然后只留下 src/main 和 build.gradle廷没,其他的文件全部刪除沟娱。
-
在main 目錄下創(chuàng)建 groovy 文件夾,然后在 groovy 目錄下就可以創(chuàng)建我們的包名和 groovy 文件了,記得后綴要以 .groovy 結(jié)尾腕柜。在這個文件中引入創(chuàng)建的包名,然后寫一個 Class 繼承于 Plugin< Project > 并重寫 apply 方法。
class MyPlugin implements Plugin<Project> { @Override void apply(Project project) { System.out.println("-----------插件開始-----------") System.out.println("---這是我們的自定義插件---") System.out.println("-----------插件結(jié)束-----------") } }
-
在 main 目錄下創(chuàng)建 resources文件夾盏缤,繼續(xù)在 resources 下創(chuàng)建 META-INF 文件夾砰蠢,繼續(xù)在 META-INF 文件夾下創(chuàng)建 gradle-plugins 文件夾,最后在 gradle-plugins 文件夾下創(chuàng)建一個 xxx.properties 文件唉铜,注意:這個 xxx 就是在 app 下的 build.gradle 中引入時的名字台舱,例如:apply plugin: ‘xxx’。在文件中寫 implementation-class=implementation-class=com.andoter.customplugin.MyPlugin潭流。
implementation-class=com.andoter.customplugin.MyPlugin
-
打開 build.gradle 刪除里面所有的內(nèi)容竞惋。然后格式按這個寫,uploadArchives 是上傳到 maven 庫灰嫉,然后執(zhí)行 uploadArchives 這個 task拆宛,就將我們的這個插件打包上傳到了本地 maven 中,可以去本地的 Maven 庫中查看讼撒。
apply plugin: 'groovy' apply plugin: 'maven' dependencies { compile gradleApi() compile localGroovy() } repositories { mavenCentral() } group = 'com.andoter.customplugin' version = '1.0' uploadArchives { repositories { mavenDeployer { repository(url: uri('../repo')) } } }
在上面的實現(xiàn)中浑厚,我們也可以把 group、version 字段配置在內(nèi)部:
uploadArchives { repositories { mavenDeployer { repository(url: uri('../repo')) pom.groupId = "com.andoter.customplugin" pom.artifactId = "groovydemo" pom.version = "1.0" } } }
-
應(yīng)用 gradle 插件:在項目下的 build.gradle(也可以在 module 中)中的 repositories 模塊中定義本地 maven 庫地址根盒。在 dependencies 模塊中引入我們的插件的路徑钳幅。
// 根目錄 .gradle 文件配置插件的地址 buildscript { repositories { google() jcenter() mavenCentral() maven { url './repo' } } //格式為-->group:module:version dependencies { classpath 'com.android.tools.build:gradle:3.1.2' classpath 'com.andoter.customplugin:groovydemo:1.0' } } // 子項目使用插件 apply plugin: 'com.andoter.customplugin'
這樣就完成一個自定義插件的使用步驟,自定義插件的核心開發(fā)一個什么樣的插件炎滞,比如結(jié)合 Transform 開發(fā)一個編譯時框架敢艰。
3. Android DSL
Android 提供對應(yīng)的 android 插件用于項目的構(gòu)建配置,在 Android 中項目的類型有以下四種:
- AppExtension:對應(yīng) com.android.application册赛。
- LibraryExtension:對應(yīng) com.android.library钠导。
- TestExtension:對應(yīng) com.android.test。
- FeatureExtension:對應(yīng) com.android.feature击奶,及時應(yīng)用辈双。
3.1 Android 插件腳本塊配置
Android 構(gòu)建系統(tǒng)編譯應(yīng)用資源和源代碼,然后將它們打包成可供測試柜砾、部署湃望、簽署和分發(fā)的 APK。Android Studio 使用 Gradle 這一高級構(gòu)建工具包來自動化執(zhí)行和管理構(gòu)建流程痰驱,同時也允許您定義靈活的自定義構(gòu)建配置证芭。每個構(gòu)建配置均可自行定義一組代碼和資源,同時對所有應(yīng)用版本共有的部分加以重復(fù)利用担映。Android Plugin for Gradle 與這個構(gòu)建工具包協(xié)作废士,共同提供專用于構(gòu)建和測試 Android 應(yīng)用的流程和可配置設(shè)置。
3.1.1 aaptOptions { }
配置 Android 資源打包工具 AAPT 選項蝇完。
aaptOptions {
additionalParameters '-S',
'/Users/yifan/dev/github/Testapp/app/src/main/res3',
'-S',
'/Users/yifan/dev/github/Testapp/app/src/main/res2',
'--auto-add-overlay'
noCompress 'foo', 'bar'
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
這個選項用于配置 AAPT 資源打包時的一些處理官硝,比如資源替換矗蕊,這塊內(nèi)容可參照編譯時替換資源。
3.1.2 adbOptions { }
配置 Android 調(diào)試工具 ADB 選項氢架。通常我們通過 adb 指令來進(jìn)行 Apk 的安裝或卸載傻咖,或者一些文件拷貝工作,通過 adbOptions {} 腳本塊同樣可以在 Android Plugin 中進(jìn)行配置岖研,adbOptions {} 腳本塊對應(yīng) AdbOptions 類卿操,該類有兩個屬性:
- installOptions:adb 配置選項,是 List<String> 類型孙援。
- timeOutInMs:是設(shè)置超時時間的害淤,單位是毫秒,這個超時時間是執(zhí)行adb這個命令的超時時間拓售,int 類型窥摄。
android {
adbOptions {
timeOutInMs 10 * 1000
installOptions '-r','-s'
}
}
- -l:鎖定該應(yīng)用程序
- -r:替換已存在的應(yīng)用程序,也就是我們說的強制安裝
- -t:允許測試包
- -s:把應(yīng)用程序安裝到 SD 卡上
- -d:允許進(jìn)行降級安裝邻辉,也就是安裝的比手機上帶的版本低
- -g:為該應(yīng)用授予所有運行時的權(quán)限
3.1.3 buildTypes { }
當(dāng)前項目的構(gòu)建類型配置溪王,對應(yīng)的配置類型 BuildType 類,在 Android Studio 的中已經(jīng)給我們內(nèi)置了 release 和 debug 兩種構(gòu)建類型值骇,這兩種模式的主要差異在于能否在設(shè)備上調(diào)試以及簽名不一樣莹菱,這里會涉及到很多屬性可以配置。
- applicationIdSuffix:配置基于應(yīng)用默認(rèn)的 applicationId 的后綴吱瘩,常用于構(gòu)建變體應(yīng)用道伟。
- consumerProguardFiles:配置 .aar 文件中是否使用 Proguard 混淆。
- crunchPngs:針對 png 的優(yōu)化使碾,設(shè)置為 true 的時候會增加編譯時間蜜徽。
- debuggable:配置構(gòu)建的 apk 是否能夠進(jìn)行 debug。
- javaCompileOptions:配置 Java 編譯的配置
- jniDebuggable:配置構(gòu)建類型的 apk native code 是否能夠進(jìn)行 debug票摇。
- minifyEnabled:是否啟用 Proguard 混淆拘鞋。
- multiDexEnabled:是否使用分包。
- multiDexKeepFile:指定放到主 dex 中的文件矢门。
- multiDexKeepProguard:配置指定的文件使用 Proguard盆色。
- proguardFiles:混淆文件。
- shrinkResources:用于配置是否自動清理未使用的資源祟剔,默認(rèn)為 false隔躲。
- signingConfig:簽名文件。
- useProguard
- versionNameSuffix:類似于 applicationIdSuffix物延。
- zipAlignEnabled:zipAlign 優(yōu)化 apk 文件的工具宣旱。
buildTypes {
release {
minifyEnabled false
crunchPngs true
debuggable false
shrinkResources true
multiDexEnabled true
multiDexKeepProguard file('proguard-rules.pro') // keep specific classes using proguard syntax
multiDexKeepFile file('multiDexKeep.txt')
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig
zipAlignEnabled true
applicationIdSuffix '.release'
versionNameSuffix '.release'
}
debug {
applicationIdSuffix '.debug'
versionNameSuffix '.debug'
}
}
3.1.4 compileOptions { }
Java 編譯選項,通常是針對 JDK 進(jìn)行編碼格式修改或者指定 JDK 的版本,對應(yīng)的類是 CompileOptions叛薯。
compileOptions {
encoding = 'utf-8'
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
3.1.5 dataBinding { }
DataBinding 配置選項浑吟,查看源碼可以發(fā)現(xiàn)對應(yīng) DataBindingOptions 類笙纤,該類包含四個屬性:
- version:版本號。
- enabled:是否可用组力。
- addDefaultAdapters:是否使用默認(rèn)的 Adapter粪糙。
- enabledForTests:是否用于 Test。
dataBinding {
enabled true
version 1.0
}
3.1.6 defaultConfig { }
defaultConfig 也是 Android 插件中常見的一個配置塊忿项,負(fù)責(zé)默認(rèn)的所有配置。同樣它是一個 ProductFlavor城舞,如果一個 ProductFlavor 沒有被特殊配置,則默認(rèn)使用 defaultFlavor 的配置家夺,比如報名、版本號榨为、版本名稱等随闺。常見的屬性有:
- applicationId:applicationId 是 ProductFlavor 的一個屬性蔓腐,用于配置 App 生成的進(jìn)程名,默認(rèn)情況下是 null回论。
- minSdkVersion:指定 Apk 支持的最低 Android 操作系統(tǒng)版本散罕。
- targetSdkVersion:用于配置 Apk 基于的 SDK 哪個版本進(jìn)行開發(fā)。
- versionCode:同樣是 ProductFlavor 的一個屬性傀蓉,配置 Apk 的內(nèi)部版本號欧漱。
- versionName:配置版本名稱。
- testApplicationId:配置測試 App 的報名葬燎,默認(rèn)情況下是 applicationId + ".test"误甚。
- testInstrumentationRunner:配置單元測試使用的 Runner,默認(rèn)是 android.test.InstrumentationTestRunner,或者可以使用自定義的 Runner萨蚕。
- signingConfig:配置默認(rèn)的簽名信息靶草,對生成的 App 簽名。
- proguardFile:用于配置使用的混淆文件岳遥。
- proguardFiles:配置混淆使用的文件奕翔,可以配置多個。
defaultConfig {
applicationId 'com.andoter.dsw'
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
signingConfigs signingConfigs.release
}
3.1.7 dexOptions { }
dex 的配置項浩蓉,通常在開發(fā)的過程中派继,我們可以通過配置 dexOptions {} 提高編譯速度,與之對應(yīng)的是 DexOptions 接口庆猫,該接口由 DefaultDexOptions 默認(rèn)實現(xiàn),DefaultDexOptions 類中包含以下屬性:
- preDexLibraries:默認(rèn) true
- jumboMode:默認(rèn) false
- dexInProcess:默認(rèn)為 true杉畜,所有的 dex 都在 process 中,提高效率
- javaMaxHeapSize:最大的堆大小
- maxProcessCount:最大的 process 個數(shù)
- threadCount:線程個數(shù)
dexOptions {
incremental true //是否增量灭袁,如果開啟multi-dex, 此句無效
preDexLibraries true
javaMaxHeapSize "4g" //java 編譯的 Heap 大小
jumboMode true
threadCount 8 //gradle輸就輸在了并行上, 都是串行, 增加線程數(shù)沒用
// 設(shè)置最大的進(jìn)程數(shù):Memory = maxProcessCount * javaMaxHeapSize
maxProcessCount 8
}
3.1.8 lintOptions { }
Lint 是Android Studio 提供的 代碼掃描分析工具,它可以幫助我們發(fā)現(xiàn)代碼結(jié)構(gòu)/質(zhì)量問題举娩,同時提供一些解決方案铜涉,而且這個過程不需要我們手寫測試用例。Lint 發(fā)現(xiàn)的每個問題都有描述信息和等級(和測試發(fā)現(xiàn) bug 很相似)盖彭,我們可以很方便地定位問題同時按照嚴(yán)重程度
進(jìn)行解決铺呵。
android {
lintOptions {
// true--關(guān)閉lint報告的分析進(jìn)度
quiet true
// true--錯誤發(fā)生后停止gradle構(gòu)建
abortOnError false
// true--只報告error
ignoreWarnings true
// true--忽略有錯誤的文件的全/絕對路徑(默認(rèn)是true)
//absolutePaths true
// true--檢查所有問題點,包含其他默認(rèn)關(guān)閉項
checkAllWarnings true
// true--所有warning當(dāng)做error
warningsAsErrors true
// 關(guān)閉指定問題檢查
disable 'TypographyFractions','TypographyQuotes'
// 打開指定問題檢查
enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
// 僅檢查指定問題
check 'NewApi', 'InlinedApi'
// true--error輸出文件不包含源碼行號
noLines true
// true--顯示錯誤的所有發(fā)生位置沪饺,不截取
showAll true
// 回退lint設(shè)置(默認(rèn)規(guī)則)
lintConfig file("default-lint.xml")
// true--生成txt格式報告(默認(rèn)false)
textReport true
// 重定向輸出;可以是文件或'stdout'
textOutput 'stdout'
// true--生成XML格式報告
xmlReport false
// 指定xml報告文檔(默認(rèn)lint-results.xml)
xmlOutput file("lint-report.xml")
// true--生成HTML報告(帶問題解釋讥脐,源碼位置魏滚,等)
htmlReport true
// html報告可選路徑(構(gòu)建器默認(rèn)是lint-results.html )
htmlOutput file("lint-report.html")
// true--所有正式版構(gòu)建執(zhí)行規(guī)則生成崩潰的lint檢查更哄,如果有崩潰問題將停止構(gòu)建
checkReleaseBuilds true
// 在發(fā)布版本編譯時檢查(即使不包含lint目標(biāo))觅捆,指定問題的規(guī)則生成崩潰
fatal 'NewApi', 'InlineApi'
// 指定問題的規(guī)則生成錯誤
error 'Wakelock', 'TextViewEdits'
// 指定問題的規(guī)則生成警告
warning 'ResourceAsColor'
// 忽略指定問題的規(guī)則(同關(guān)閉檢查)
ignore 'TypographyQuotes'
}
}
3.1.9 packagingOptions { }
Android 打包配置項,可以配置打包的時候哪些打包進(jìn) Apk赢赊。當(dāng)項目中依賴的第三方庫越來越多時,有可能會出現(xiàn)兩個依賴庫中存在同一個
(名稱)文件玩讳。如果這樣熏纯,Gradle在打包時就會提示錯誤(警告)。那么就可以根據(jù)提示往扔,然后使用以下方法將重復(fù)的文件剔除吭服。
packagingOptions {
//這個是在同時使用butterknife艇棕、dagger2做的一個處理沼琉。同理脓鹃,遇到類似的問題,只要根據(jù)gradle的提示妆档,做類似處理即可。
exclude 'META-INF/services/javax.annotation.processing.Processor'
}
3.1.10 productFlavors { }
用于構(gòu)建不同的產(chǎn)品風(fēng)味碰镜,在上面我們提到 defaultConfig{} 也是一種產(chǎn)品風(fēng)味,可作為所有產(chǎn)品風(fēng)味的“基類”共同部分杰刽。風(fēng)味(Flavor) 對應(yīng) ProductFlavor 類滓鸠,該類的屬性與配置屬性相匹配。
android {
...
defaultConfig {...}
buildTypes {...}
productFlavors {
demo {
applicationIdSuffix ".demo"
versionNameSuffix "-demo"
}
full {
applicationIdSuffix ".full"
versionNameSuffix "-full"
}
}
}
在創(chuàng)建和配置您的產(chǎn)品風(fēng)味之后珠月,在通知欄中點擊 Sync Now啤挎。同步完成后,Gradle會根據(jù)您的構(gòu)建類型和產(chǎn)品風(fēng)味自動創(chuàng)建構(gòu)建變體伙判,
并按照 <product-flavor><Build-Type>的格式命名這些變體宴抚。例如危虱,如果您創(chuàng)建了“演示”和“完整”這兩種產(chǎn)品風(fēng)味并保留默認(rèn)的“調(diào)試”和“發(fā)布”構(gòu)建類型,Gradle 將創(chuàng)建以下構(gòu)建變體:
- 演示調(diào)試
- 演示發(fā)布
- 完整調(diào)試
- 完整發(fā)布
您可以將構(gòu)建變體更改為您要構(gòu)建并運行的任何變體,只需轉(zhuǎn)到 Build > Select Build Variant延届,然后從下拉菜單中選擇一個變體厕吉。
產(chǎn)品風(fēng)味組合
通常在適配多個渠道的時候龄减,需要為特定的渠道做部分特殊的處理署隘,這里就會涉及到結(jié)合 buildTypes{} 組合不同的產(chǎn)品風(fēng)味組合。
android {
...
buildTypes {
debug {...}
release {...}
}
// Specifies the flavor dimensions you want to use. The order in which you
// list each dimension determines its priority, from highest to lowest,
// when Gradle merges variant sources and configurations. You must assign
// each product flavor you configure to one of the flavor dimensions.
flavorDimensions "api", "mode"
productFlavors {
demo {
// Assigns this product flavor to the "mode" flavor dimension.
dimension "mode"
...
}
full {
dimension "mode"
...
}
// Configurations in the "api" product flavors override those in "mode"
// flavors and the defaultConfig {} block. Gradle determines the priority
// between flavor dimensions based on the order in which they appear next
// to the flavorDimensions property above--the first dimension has a higher
// priority than the second, and so on.
minApi24 {
dimension "api"
minSdkVersion '24'
// To ensure the target device receives the version of the app with
// the highest compatible API level, assign version codes in increasing
// value with API level. To learn more about assigning version codes to
// support app updates and uploading to Google Play, read Multiple APK Support
versionCode 30000 + android.defaultConfig.versionCode
versionNameSuffix "-minApi24"
...
}
minApi23 {
dimension "api"
minSdkVersion '23'
versionCode 20000 + android.defaultConfig.versionCode
versionNameSuffix "-minApi23"
...
}
minApi21 {
dimension "api"
minSdkVersion '21'
versionCode 10000 + android.defaultConfig.versionCode
versionNameSuffix "-minApi21"
...
}
}
}
Gradle 創(chuàng)建的構(gòu)建變體數(shù)量等于每個風(fēng)味維度中的風(fēng)味數(shù)量與您配置的構(gòu)建類型數(shù)量的乘積畅哑。在 Gradle 為每個構(gòu)建變體或?qū)?yīng) APK 命名時砂客,屬于較高優(yōu)先級風(fēng)味維度的產(chǎn)品風(fēng)味首先顯示,之后是較低優(yōu)先級維度的產(chǎn)品風(fēng)味彤恶,再之后是構(gòu)建類型钞钙。以上面的構(gòu)建配置為例,Gradle 可以使用以下命名方案創(chuàng)建總共 12 個構(gòu)建變體:
構(gòu)建變體:[minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
對應(yīng) APK:app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
同樣我們在 Terminal 中通過 gradle 指令執(zhí)行構(gòu)建變體的任務(wù):變體名稱(flavorDimensions 的第一個維度的 Flavor) + flavorDimensions 的別的維度的 Flavor + buildTypes声离,例如:minApi24DemoDebug芒炼,構(gòu)建出來的對應(yīng) Apk:app-minApi24-demo-debug.apk
過濾變體
Gradle 會自動根據(jù)設(shè)置的 buildTypes{} 和 flavorDimensions{} 創(chuàng)建很多變體的組合體,但是有些是我們不需要的术徊,這里就涉及到過濾變體的操作本刽。Gradle 提供了 variantFilter {} 腳本塊來過濾笋除,腳本塊對應(yīng) VariantFilter 接口墙歪,接口中包含:
- setIgnore(boolean ignore):設(shè)置是否忽略某個變體
- getBuildType:獲取變體的構(gòu)建類型
- List<ProductFlavor> getFlavors():返回所有變體的列表
- getName:獲取變體的 name 名稱
variantFilter { variant ->
def names = variant.flavors.name
// To check for a certain build type, use variant.buildType.name == "<buildType>"
if (names.contains("minApi23") && names.contains("demo")) {
// Gradle ignores any variants that satisfy the conditions above.
setIgnore(true)
} else {
setIgnore(false)
}
}
更多關(guān)于構(gòu)建變體內(nèi)容參照官方:構(gòu)建變體
3.1.11 signingConfigs { }
配置簽名信息址愿,常用于 BuildType 和 ProductFlavor 配置跋炕,在構(gòu)建變體的過程中,會出現(xiàn)很多種類德召,所以針對不同類別的變體所使用的簽名可能也是不同的敬锐,這就需要使用 signingConfigs{} 配置簽名信息合集赞赖,然后按需所取。
signingConfigs {
release {//發(fā)布版本的簽名配置
storeFile file(props['KEYSTORE_FILE'])
keyAlias props['KEY_ALIAS']
storePassword props['KEYSTORE_PWD']
keyPassword props['KEY_PWD']
}
debug {//調(diào)試版本的簽名配置
storeFile file(props['DEBUG_KEYSTORE'])
keyAlias props['DEBUG_ALIAS']
storePassword props['DEBUG_KEYSTORE_PWD']
keyPassword props['DEBUG_KEY_PWD']
}
}
buildTypes {
signingConfig signingConfigs.release
}
3.1.12 sourceSets { }
在 AndroidStudio 中,在 src/main/java 目錄下創(chuàng)建我們的 .java 文件顾复,這些都是系統(tǒng)通過 sourceSet{} 設(shè)置好的,比如我們在外部創(chuàng)建一個文件夾,選中文件夾右鍵就無創(chuàng)建 .java 文件的選項站欺。就需要我們通過 sourceSets 進(jìn)行配置,腳本塊對應(yīng) AndroidSourceSet 接口蟆技,接口中有:
- AndroidSourceSet java(Closure configureClosure):配置 java 文件存放路徑
- AndroidSourceSet resources(Closure configureClosure):配置 resource 目錄
- AndroidSourceSet jniLibs(Closure configureClosure):配置 jniLibs 目錄
- AndroidSourceSet jni(Closure configureClosure):配置 jni 文件目錄
- AndroidSourceSet renderscript(Closure configureClosure):配置 renderscript 目錄
- AndroidSourceSet aidl(Closure configureClosure):配置 aidl 文件目錄
- AndroidSourceSet assets(Closure configureClosure):配置 assets 目錄
- AndroidSourceSet res(Closure configureClosure):配置 res 目錄
- AndroidSourceSet manifest(Closure configureClosure):配置 manifest 目錄
sourceSets {
main {
java {
srcDir 'src/main/testjava'
}
resources {
srcDir 'src/resources'
}
}
}
3.1.13 splits { }
拆分機制比起使用 flavors界酒,能讓應(yīng)用程序更有效地構(gòu)建一些形式的多個apk寞肖。
多 apk 只支持以下類型:
- 屏幕密度
- ABI
腳本塊對應(yīng) Splits 類琼稻,該類中有三個屬性:
- density:像素密度
- abi:abi 類型
- language:語言
splits {
density {
enable true
exclude "ldpi", "tvdpi", "xxxhdpi"
compatibleScreens 'small', 'normal', 'large', 'xlarge'
}
abi {
enable true
reset()
include 'x86', 'armeabi-v7a', 'mips'
universalApk true
}
}
4. 發(fā)現(xiàn)好資料
Gradle 的功能非常強大横殴,這里也僅僅針對 Android 項目構(gòu)建常用的進(jìn)行總結(jié)瞄崇,Gradle 還可用于單元測試制妄,或者用于別的項目構(gòu)建,上傳構(gòu)建很多部分诗芜。具體可以參照官方 API 文檔瞳抓。