gradle使用的腳本語(yǔ)言是Groovy,Groovy完全兼容java
DSL
DSL的意思是領(lǐng)域特定語(yǔ)言,即專注于一個(gè)領(lǐng)域的語(yǔ)言,而像java是一種通用全面的語(yǔ)言,而Gradle就是一門DSL,基于Groovy,專注于自動(dòng)化構(gòu)建.
基本用法
def 用于參數(shù)/方法的定義,定義可不用定義返回類型
<< 此符號(hào)隊(duì)伍任務(wù)來(lái)說(shuō)相當(dāng)于doLast,將任務(wù)放到任務(wù)隊(duì)列的隊(duì)尾執(zhí)行
Groovy的字符串表示
Groovy的單引號(hào)和雙引號(hào)都表示字符串,但單引號(hào)不具有計(jì)算功能,雙引號(hào)有,如

但單引號(hào)和雙引號(hào)都可以使用"+"連接字符串,如

Groovy支持java的全部集合類
List:
聲名定義
def num = [1,2,3,4];//相當(dāng)于java的List<Interger> num = new ArrayList()<>;
使用時(shí)可像數(shù)組一樣通過(guò)下標(biāo)獲取,正數(shù)為從0開(kāi)始算,負(fù)數(shù)為從末尾開(kāi)始算
num[0]為1 num[-1]為4 num[1..3]為訪問(wèn)1到3位置的數(shù)據(jù)
遍歷list使用each
num.each{
println it //it為迭代元素
}
Map
聲名定義(看起來(lái)像鍵值對(duì)的數(shù)組):
def num = ['width':1,'height':2];
使用時(shí)可使用num['width']和num.width的方式來(lái)獲取
遍歷也是使用each,但其迭代的元素it為一個(gè)Entry實(shí)例
num.each{
println "Key:${it.key},Value:${it.value}"
}
Groovy的方法調(diào)用
Groocy的方法調(diào)用不需要括號(hào)如add(1,2)可為add 1,2
返回值不用return如
def add(int i,int j){
i+j//此處作為最后一行執(zhí)行,會(huì)被識(shí)別為返回的參數(shù)
}
實(shí)體類bean
groovy的實(shí)體類不需要寫(xiě)get/set方法,在定義參數(shù)后,內(nèi)部會(huì)自動(dòng)有這樣個(gè)方法,也意味著其外部可讀可寫(xiě),如果不定義參數(shù),直接寫(xiě)get方法,那這個(gè)參數(shù)為外部只讀
class Present{
private String name//定義字符串name,可讀可寫(xiě)
public int getAge(){//定義int的age,只讀
12
}
}
閉包
方法傳入的參數(shù)為一個(gè)代碼塊,在代碼塊的調(diào)用的方法可被指定優(yōu)先調(diào)用那個(gè)對(duì)象的方法,也就是閉包委托.閉包的關(guān)鍵字為Closure,如上述使用的each傳入的為代碼塊,其中就位閉包,而it為閉包的委托.
單參數(shù)
task test{
customEach{
println it
}
def customEach(closure){
for(int i in 1..10){//遍歷1到10
closure(i)//相當(dāng)于遍歷一次就調(diào)用一次傳進(jìn)來(lái)的代碼塊,傳入的i就為it的值
}
}
}
多參數(shù)
而且通過(guò)閉包可傳出多個(gè)參數(shù),其實(shí)是傳出了一個(gè)對(duì)象.
task test{
eachMap{k,v ->//指定多個(gè)參數(shù)的代表
println "${k},${v}"
}
def eachMap(closure){
def mapl = ["a":1,"b":2]
mapl.each{
closure(it.key,it.value)
}
}
}
閉包委托
閉包中有3個(gè)關(guān)鍵字(屬性),thisObject(相當(dāng)于Android的this),owner,delegate,默認(rèn)下owner和delegate相等,但delegate可被更改,更改后會(huì)方法塊中的調(diào)用會(huì)調(diào)用其指向的對(duì)象.
preson{
age = 10//此處相當(dāng)于p.age = 10,p為閉包代理
}
def preson(Closure<Person> closure){
Preson p = new Preson();
closure.delegate = p
closure.setResolveStrategy(Closure.DELEGATE_FIRST);//設(shè)置委托模式優(yōu)先
closure(p)
}
settings.gradle文件
用于配置工程樹(shù),配置工程中要加入構(gòu)建的工程/模塊,并可設(shè)置對(duì)應(yīng)工程的路徑,不設(shè)置就默認(rèn)為工程跟根路徑.
include ':app'
include ':test'
project(':test').projectDir = new File('test')//設(shè)置test工程的目錄在./test中
build.gradle文件
每個(gè)工程都有一個(gè)build文件,用于配置構(gòu)建時(shí)導(dǎo)入的插件或者配合構(gòu)建參數(shù),其為工程構(gòu)建的入口,其中有一個(gè)根工程的build,在此文件中可統(tǒng)一配置所有子工程的配置.
其中allprojects和subprojects都是對(duì)子工程的配置,buildProject時(shí)對(duì)導(dǎo)入的工程的配置,如導(dǎo)入a模塊,而a模塊需要導(dǎo)入b模塊.配置使用的方法基本都是用了閉包,所以也可以在配置的時(shí)候輸出信息.
任務(wù)依賴
在進(jìn)行task時(shí),有時(shí)需要不同任務(wù)會(huì)有依賴關(guān)系,所以存在誰(shuí)前誰(shuí)后的問(wèn)題,(關(guān)鍵字dependsOn)如a依賴于b,所以b要先執(zhí)行完a才執(zhí)行,如
task b{
xxx
}
task a(dependsOn b){
xxxxx
}
以上任務(wù),a會(huì)等b運(yùn)行完了再運(yùn)行
也可以依賴多個(gè)任務(wù),如a依賴b,c任務(wù)
task c{
xxx
}
task b{
xxx
}
task a{
dependsOn c,b
xxx
}
每個(gè)任務(wù)在project中都為一個(gè)屬性,所以可以調(diào)用這個(gè)屬性如
c.doLast{
xxx
}
c.doFirst{
xxx
}
此處調(diào)用了c的兩個(gè)方法,他們的first和last時(shí)在c任務(wù)運(yùn)行后的
自定義prohect屬性
使用ext來(lái)自定義屬性
ext age = 5//定義一個(gè)屬性
ext {//定義多個(gè)屬性
name = '666'
address = '你很棒'
}
定義的屬性相當(dāng)于全局變量
任務(wù)分組和描述
分組即描述是為了更好的管理這些任務(wù)
task.group = BasePlugin.BUID_GROUP//將任務(wù)分組到BUID_GROUP中
task.description = '測(cè)試用的'//添加任務(wù)描述,說(shuō)明任務(wù)的作用
任務(wù)的執(zhí)行
每個(gè)任務(wù)內(nèi)都有一個(gè)List列表,保存的就是任務(wù)要執(zhí)行的action,所以doFirst和doLast是對(duì)List添加action在列表的頭部或尾部,然后從頭到尾執(zhí)行,這里還有和doSelf是中間,也是任務(wù)本身.
可以用任務(wù)排序來(lái)控制任務(wù)的執(zhí)行順序,聽(tīng)說(shuō)這個(gè)為base版,后期可能更改
task.shouldRunAfter(task2)//task建議在task2之后執(zhí)行,這個(gè)用處不大,實(shí)際運(yùn)行可能會(huì)也可能不會(huì)
task.mustRunAfter(task2)//task一定在task2之后執(zhí)行
任務(wù)啟用
任務(wù)中有個(gè)enabled參數(shù),用于控制任務(wù)是否啟用,默認(rèn)為true啟用,設(shè)置為false是不執(zhí)行,并提示跳過(guò)此任務(wù)
task not{
xzxxx
}
not.enabled = false
斷言
groovy中除了可以使用if else來(lái)斷言,還提供了onlyIf,其作用于任務(wù)的條件執(zhí)行,onlyIf接收一個(gè)方法塊的返回值,true則執(zhí)行此任務(wù),否則不執(zhí)行
task.onlyIf{
Object build = project.property("build")
if(build.equals("666")){
true
}
false
}
以上任務(wù)開(kāi)啟時(shí)使用命令傳入build參數(shù)
./gradlew -p build=666 :task
任務(wù)規(guī)則
在gradle任務(wù)執(zhí)行出現(xiàn)錯(cuò)誤,無(wú)法識(shí)別該任務(wù)或找不到時(shí),會(huì)調(diào)用這個(gè)規(guī)則.
addRule("測(cè)試規(guī)則"){
String taskName->
task(taskName){
println "測(cè)試出現(xiàn)個(gè)提示"
}
}
findByName("666")//查找時(shí),出現(xiàn)無(wú)此任務(wù)時(shí)會(huì)觸發(fā)規(guī)則,發(fā)出提示
gradle插件
應(yīng)用二進(jìn)制插件
apply plugin:a//應(yīng)用a插件,這里的a建議用全限定名
腳本插件
將腳本加載到本gradle中
build.gradle
apply from:'a'
a.gradle
ext{
version = '1.0.0'
name = 'test'
}
相當(dāng)于build中就有了a中的參數(shù)
apply也可使用閉包
apply {
plugin :xxx
plugin : xxx
...
}
應(yīng)用第三方插件
在應(yīng)用二進(jìn)制第三方插件時(shí),需要配置buildscript中的dependencies中的classpath,指定使用的gradle版本.若此插件被官網(wǎng)托管了就可以不用設(shè)置.

依賴第三方
compile 編譯時(shí)依賴
runtime 運(yùn)行時(shí)依賴
testCompile 編譯測(cè)試時(shí)依賴,打包不會(huì)依賴進(jìn)去
testRuntime 運(yùn)行測(cè)試時(shí)依賴
archives 項(xiàng)目發(fā)布構(gòu)建(jar)依賴
default 默認(rèn)依賴
也可以指定一個(gè)源集依賴
mainCompile //main源集依賴
compile project(':a')//依賴a工程
compile files('libs/a.jar')//依賴jar包
compile fileTree(dir:'libs',include:'*.jar')//依賴libs目錄下所有jar包
源集
sourceSets,用于設(shè)置資源的配置,如資源所在的路徑和編譯后的路徑
其中的屬性
name//只讀,如main
output.classesDir//指定源集編譯后的class目錄
output.resourcess//指定源集編譯后生成的資源目錄
compileClasspath//編譯指定源集時(shí)所需的classpath
java//指定源集java源文件
java.srcDirs//指定源集java源文件所在目錄
resources//指定源集的資源文件
resources.secDirs//指定源集的資源文件目錄
sourceSets{
main{
java{
scrDir 'src/java' // 設(shè)置main源集的java源文件的目錄
}
}
}


Android的Gradle
各屬性
compileSdkVersion//編譯的androidSDK版本,可為int或String
BuildToosVersion//構(gòu)建版本,23.0.1
defaultConfig//默認(rèn)配置,配置生產(chǎn)模式,多渠道打包
buildTypes//配置源文件資源文件,混淆,文件目錄
defaultConfig配置
applicationId//指定生成的報(bào)名,默認(rèn)為null,會(huì)從manifest中讀取
minSdkVersion//App支持的最低Android版本
targetSdkVersion//App是基于那個(gè)android版本開(kāi)發(fā)的,默認(rèn)為null,會(huì)從manifest讀取
versionCode//App內(nèi)部版本,給內(nèi)部人員看的
versionName//與versionCode類似,但其是給用戶看的外部版本
testApplicationId//測(cè)試App的包名,一般使用默認(rèn)的,applicationId+'test'
testInstructionRunner//配置單元測(cè)試使用的Runner,默認(rèn)為android.test.InstrumentationTestRunner
proguardFile//設(shè)置一個(gè)混淆文件
proguardFiles//設(shè)置多個(gè)混淆文件
signingConfig//配置簽名信息
signingConfigs//配置多個(gè)簽名信息
android{
compileSdkVersion 23
buildToolsVersion "23.0.1"
signingConfigs{
release{//正式版
storeFile file("myreleasekey.keystore")//設(shè)置簽名文件
storePassword "password"http://簽名的store密碼
keyAlias "Alias"http://簽名的Alias
keyPassword "password"http://簽名的key密碼
}
debug{//debug版
....
}
}
defaultConfig{
applicationId "cn.com.lewis_v.test"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0.1"
//signingConfig signingConfigs.debug//使用Debug簽名
}
}
buildTypes{
release{
signingConfig signingConfigs.release//此處也能設(shè)置簽名
}
debug{
signingConfig.signingConfigs.debug
}
}
buildTypes配置
applicationIdSuffix//配置applicationId的后綴,相當(dāng)于改包名了
debuggable//配置生成的apk是否可調(diào)試
jniDebuggable//配置生成的apk是否可進(jìn)行jni調(diào)試
minifyEnabled//是否啟用混淆
multiDexEnabled//是否啟用分包,方法超過(guò)65535時(shí)需要拆分多個(gè)包
proguardFile//配置混淆文件
proguardFile//配置多個(gè)混淆文件
shrinkResources//是否自動(dòng)清理未使用的資源,默認(rèn)false
signingConfig//配置簽名信息
testFunctionalTest//是否為功能測(cè)試
testHandleProfiling//是否啟用分析功能
useJack//是否啟用,新的編譯器,這個(gè)編譯器更快,但是目前還不成熟
可以看到buildTypes和defaultConfig有些屬性重復(fù)了,但一般默認(rèn)配置在前,然后在buildType中根據(jù)不同渠道去修改相關(guān)屬性,未設(shè)置的就是用默認(rèn)配置
zipalign優(yōu)化
android提供的整理優(yōu)化apk的工具,可提高運(yùn)行效率,降低內(nèi)存使用,使用方法很簡(jiǎn)單
android{
buildTypes{
release{
zipAlignEnabled true//啟用zipAlign
}
debug{
...
}
}
}
使用共享庫(kù)
如在6.0之后httpClient被刪除了,要使用的話需要手動(dòng)添加共享庫(kù),build.gradle中為
android{
useLibrary('org.apache.http.legacy')
....
}
manifest.xml,這里不寫(xiě)不會(huì)出錯(cuò)
<uses-library
android:name="org.apache.http.legacy"
android:required="true"
gradle中使用命令行
gradle提供了exec來(lái)執(zhí)行shell命令,
def stdout = new ByteArrayOutputStream()
exec{
commandLine 'git','tag','--list'
standardOutput = stdout//獲取命令執(zhí)行的返回
}
return stdout.toString()
動(dòng)態(tài)設(shè)置Manifest
可使用Gradle動(dòng)態(tài)替換manifest中${}占位符
manifest.xml
<meta-data android:value="${TEST}" android:name="test"/>
在gradle中使用manifestPlaceholders來(lái)替換
gradle
productFlavors{
google{
manifestPlaceholders.put("TEST","google")
}
baidu{
manifestPlaceholders.put("TEST","baidu")
}
}
可使用遍歷所有productFlavors來(lái)替換,這樣可能沒(méi)那么靈活,但是對(duì)于很多渠道且不通點(diǎn)可在productFlavors中獲取的時(shí)候,使用會(huì)很方便
productFlavors.all{
google{
}
baidu{
}
}
productFlavors.all{flavor->
manifestPlaceholders.put("TEST",name)//此處的那么是閉包名/任務(wù)名,也就是上面的google和baidu
}
BuildConfig
BuildConfig中包含了基本版本信息,如版本號(hào),渠道,是否為DEBUG模式,其不可修改,而且是自動(dòng)生成的
如平常獲取包名使用context.getPackageName(),這里面實(shí)現(xiàn)較為復(fù)雜,性能不高,而使用BuildConfig.APPLICATION_ID獲取到的就很方便,且其為一個(gè)全局的靜態(tài)變量,獲取的性能高
而其中的BuildConfig.DEBUG,標(biāo)記是否為DEBUG模式,是則為true,不是則為false
當(dāng)然也可以在構(gòu)建的時(shí)候向BuildConfig加入我們自定義的參數(shù),格式為:
BuildConfigField 'type','name','value'//依次為類型,參數(shù)名,參數(shù)值
buildTypes{
release{
BuildConfigField 'String','HOST','"http://www.baidu.com"'
}
debug{
BuildConfigField 'String','HOST','"http://www.google.com"'
}
}
這樣,在正式包下,BuildConfig.HOST的值為baidu的,測(cè)試包下為google,這里需要注意的是參數(shù)值為String的需要有雙引號(hào)'"xxxx"',如果寫(xiě)成了'xxx',會(huì)出錯(cuò),原因是他在文件中會(huì)寫(xiě)成
public static final String HOST = www.baidu.com;//這樣會(huì)出錯(cuò),因?yàn)楹竺娴牟粫?huì)被識(shí)別為字符串
public static final String HOST = "www.baidu.com";//這樣才是正確的
自定義資源文件的內(nèi)容
一般使用的資源文件,如string.xml,color.xml等,都可以在gradle中動(dòng)態(tài)修改,在buildTypes和productFlavors中都可以修改
使用resValue 'type','name','value'//這里和BuildConfig類似,type為資源類型,如string,name是資源中的標(biāo)識(shí)名字,value是要設(shè)置為什么值
productFlavors{
google{
resValue 'string','app_name','google'
}
baidu{
resvalue 'string','app_name','baidu'
}
}
上述代碼,可在不同渠道下,修改string.xml中的app_name的值
JAVA編譯選項(xiàng)
為設(shè)置java編譯的JDK版本,文件編碼,在android{}中提供了compileOption來(lái)設(shè)置這些,其中只提供了三個(gè)屬性encoding(編碼格式),sourceCompatibility和targetCompatibility為JDK版本號(hào)
android{
compileOptions{
encoding = 'utf-8'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
adb操作配置
gradle中提供了對(duì)adb命令的配置,其中配置的屬性為兩個(gè)timeOutInMs(超時(shí)),installOptions(安裝配置)
這個(gè)也是android{}中提供的adbOptions設(shè)置的
android{
adbOptions{
timeOutInMs = 5*1000//5秒超時(shí)
installOptions '-r','-s'//安裝配置
}
}
其中timeOutInMs為adb命令反應(yīng)超時(shí),在指定時(shí)間內(nèi)無(wú)反饋則視為超時(shí),報(bào)錯(cuò)CommandRejectException
installOptions安裝的命令配置,
-l:鎖定該應(yīng)用程序;
-r:強(qiáng)制安裝;
-t:允許測(cè)試包;
-s:安裝到SD卡上;
-d:允許降級(jí)安裝;-
g:授予運(yùn)行時(shí)權(quán)限;
dex選項(xiàng)
配置dex命令運(yùn)行的選項(xiàng)
android{
javaMaxHeapSize "2g"http://調(diào)用dx命令是,分配最大的堆內(nèi)存,看電腦的配置
incremental true//開(kāi)啟增量模式,默認(rèn)為false
jumboMode true//開(kāi)啟jumbo模式,用于突破65535方法數(shù)限制
threadCount 2//運(yùn)行dx命令時(shí)的線程數(shù)
}
資源自動(dòng)清理
在工程中,會(huì)應(yīng)用第三方資源或有自己的代碼不使用的,所以在gradle中提供打包時(shí)不將無(wú)用資源打包的設(shè)置
shrink
android{
buildTypes{
release{
shrinkResources true//開(kāi)啟自動(dòng)清理,默認(rèn)為false
}
}
}
開(kāi)啟很簡(jiǎn)單,但是其清理會(huì)清理未被引用的資源,但是有些使用反射的會(huì)識(shí)別不到,所以gradle提供了keep文件來(lái)設(shè)置不清理的文件,res/raw/keep.xml(不存在需要自己新建文件)
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
tools:keep="@layout/xxx*,@layout/xx"
tools:shrinkMode="safe"
/>
其中keep為不清理文件,支持*通配符,用逗號(hào)分隔文件
shrinkMode為清理模式,默認(rèn)safe,一般用safe就好了
resConfig
resConfig可配置只需要什么資源
android{
defaultConfig{
resConfig 'zh'
}
}
修改發(fā)布的aar包
修改發(fā)布的arr包
android{
defaultPublishConfig "dubug"
}
發(fā)布多個(gè)arr包
android{
publishNonDefault true//開(kāi)啟多個(gè)arr包
}
dependencies{
flavo1Compile project(path:':lib1',configuration:'flavor1Release')
flavo2Compile project(path:':lib1',configuration:'flavor2Release')
}
Junit測(cè)試
這個(gè)好像也叫單元測(cè)試,針對(duì)純java的測(cè)試
使用單元測(cè)試需要導(dǎo)入Junit依賴,不過(guò)一般工程都自動(dòng)導(dǎo)入了.
dependencies {
testImplementation 'junit:junit:4.12'
}
在java目錄下有三個(gè)包,其中有一個(gè)為test的另兩個(gè)為主程序和androidTest(這是android環(huán)境的測(cè)試)
test包下的就是單元測(cè)試方法的編輯
例如要測(cè)試一個(gè)自己的方法
public class TestUtil {
public String get(){
return "777";
}
}
在test中新建一個(gè)類用于單元測(cè)試MyTest.java,這個(gè)代碼不會(huì)加入apk包中
import org.junit.Test;
import static org.junit.Assert.*;
public class MyTest {
@Test
public void test(){
assertEquals(new TestUtil().get(),"666");
}
}
其中@Test為聲名此方法為測(cè)試方法,聲名后再單元測(cè)試的時(shí)候會(huì)調(diào)用此方法進(jìn)行測(cè)試
還有assert系列方法,這里的assertEquals是判斷兩個(gè)參數(shù)是否相同,不同會(huì)有錯(cuò)誤提示
在命令框使用 gradle test 進(jìn)行單元測(cè)試,測(cè)試結(jié)果會(huì)在app/build/reports/tests/testDebugUnitTest/classes中已HTML的形式保存,這了一個(gè)測(cè)試類會(huì)生成一個(gè)HTML文件
測(cè)試配置
可配置測(cè)試結(jié)果數(shù)據(jù)的目錄
android{
testOptions{
reportDir = "$project.buildDir/app/report"http://$project.buildDir為工程更目錄
resultsDir = "$project.buildDir/app/result"
}
}
代碼覆蓋率
檢測(cè)測(cè)試用例的覆蓋率
需要在buildTypes中開(kāi)啟testCoverageEnabled,并導(dǎo)入jacoco
...
apply plugin: 'jacoco'
...
android{
...
buildTypes{
debug{
testCoverageEnabled true
}
}
...
}
...
jacoco{
toolVersion = "0.7.1.201405082137"
}
使用命令進(jìn)行測(cè)試 gradle createDebugCoverageReport
結(jié)果在app\build\reports\coverage\debug\index.html中
Lint測(cè)試
檢查哪些代碼沒(méi)被使用,哪些使用新API等,生成一個(gè)報(bào)告,告訴哪里需要優(yōu)化.
在gradle中使用lintOptions進(jìn)行配置
android{
lintOptions{
abortOnError true//發(fā)現(xiàn)錯(cuò)誤是否退出構(gòu)建
absolutePaths false//錯(cuò)誤的輸出是否顯示絕對(duì)路徑,默認(rèn)為相對(duì)路徑
check 'NewApi'//設(shè)置需要檢測(cè)哪些Lint檢查,具體項(xiàng)目使用命令查看lint --list
checkAllWarning true//是否檢測(cè)所有警告的issue
checkReleaseBuilds true//在release中是否檢測(cè)致命錯(cuò)誤,出現(xiàn)錯(cuò)誤終止構(gòu)建
disable 'NewApi'//關(guān)閉哪些issue檢查
enable 'NewApi'//開(kāi)啟哪些檢查
explainIssues true//錯(cuò)誤報(bào)告是否包含解釋說(shuō)明
htmlOutput new File("xxx")//配置Html報(bào)告的輸出路徑
htmlReport true//是否生成html報(bào)告
ignoreWarnings false//是否忽略警告級(jí)別的檢查
lintConfig new File("xxx")//指定Lint的配置文件,一個(gè)Xml文件
noLines true//錯(cuò)誤信息中,是否不包含源代碼中的行號(hào)
quiet false//是否安靜模式,安靜模式不會(huì)顯示分析進(jìn)度
severityOverrides//返回一個(gè)Map結(jié)果,內(nèi)容為個(gè)issue的優(yōu)先級(jí)
showAll true//是否顯示全部輸出,不為true,較長(zhǎng)的信息會(huì)被截?cái)?textOutput new File("xxx")//生成text報(bào)告的路徑
textReport false//是否生成text報(bào)告
warningAsErrors false//所有警告是否當(dāng)成錯(cuò)誤處理
xmlOutput new File("xxx")//xml報(bào)告輸出路徑
xmlReport true//是否生成xml報(bào)告
}
}