[小白裝逼] Android Gradle學習筆記

gradle使用的腳本語言是Groovy,Groovy完全兼容java

DSL

DSL的意思是領域特定語言,即專注於一個領域的語言,而像java是一種通用全面的語言,而Gradle就是一門DSL,基於Groovy,專注於自動化構建.

基本用法

def 用於參數/方法的定義,定義可不用定義返回類型
<< 此符號隊伍任務來說相當於doLast,將任務放到任務隊列的隊尾執行

Groovy的字符串表示

Groovy的單引號和雙引號都表示字符串,但單引號不具有計算功能,雙引號有,如
這裏寫圖片描述
但單引號和雙引號都可以使用”+”連接字符串,如
這裏寫圖片描述

Groovy支持java的全部集合類

List:

聲名定義

def num = [1,2,3,4];//相當於java的List<Interger> num = new ArrayList()<>;

使用時可像數組一樣通過下標獲取,正數爲從0開始算,負數爲從末尾開始算
num[0]爲1 num[-1]爲4 num[1..3]爲訪問1到3位置的數據
遍歷list使用each

num.each{
println it  //it爲迭代元素
}

Map

聲名定義(看起來像鍵值對的數組):

def num = ['width':1,'height':2];

使用時可使用num[‘width’]和num.width的方式來獲取
遍歷也是使用each,但其迭代的元素it爲一個Entry實例

num.each{
println "Key:${it.key},Value:${it.value}"
}

Groovy的方法調用

Groocy的方法調用不需要括號如add(1,2)可爲add 1,2
返回值不用return如

def add(int i,int j){
i+j//此處作爲最後一行執行,會被識別爲返回的參數
}

實體類bean

groovy的實體類不需要寫get/set方法,在定義參數後,內部會自動有這樣個方法,也意味着其外部可讀可寫,如果不定義參數,直接寫get方法,那這個參數爲外部只讀

class Present{
private String name//定義字符串name,可讀可寫

public int getAge(){//定義int的age,只讀
12
}
}

閉包

方法傳入的參數爲一個代碼塊,在代碼塊的調用的方法可被指定優先調用那個對象的方法,也就是閉包委託.閉包的關鍵字爲Closure,如上述使用的each傳入的爲代碼塊,其中就位閉包,而it爲閉包的委託.

單參數

task test{
customEach{
println it
}
def customEach(closure){
for(int i in 1..10){//遍歷1到10
closure(i)//相當於遍歷一次就調用一次傳進來的代碼塊,傳入的i就爲it的值
}
}
}

多參數

而且通過閉包可傳出多個參數,其實是傳出了一個對象.

task test{
eachMap{k,v ->//指定多個參數的代表
println "${k},${v}"
}
def eachMap(closure){
    def mapl = ["a":1,"b":2]
mapl.each{
closure(it.key,it.value)
}
}
}

閉包委託

閉包中有3個關鍵字(屬性),thisObject(相當於Android的this),owner,delegate,默認下owner和delegate相等,但delegate可被更改,更改後會方法塊中的調用會調用其指向的對象.

preson{
age = 10//此處相當於p.age = 10,p爲閉包代理
}

def preson(Closure<Person> closure){
Preson p = new Preson();
closure.delegate = p
closure.setResolveStrategy(Closure.DELEGATE_FIRST);//設置委託模式優先
closure(p)
}

settings.gradle文件

用於配置工程樹,配置工程中要加入構建的工程/模塊,並可設置對應工程的路徑,不設置就默認爲工程跟根路徑.

include ':app'
include ':test'

project(':test').projectDir = new File('test')//設置test工程的目錄在./test中

build.gradle文件

每個工程都有一個build文件,用於配置構建時導入的插件或者配合構建參數,其爲工程構建的入口,其中有一個根工程的build,在此文件中可統一配置所有子工程的配置.
其中allprojects和subprojects都是對子工程的配置,buildProject時對導入的工程的配置,如導入a模塊,而a模塊需要導入b模塊.配置使用的方法基本都是用了閉包,所以也可以在配置的時候輸出信息.

任務依賴

在進行task時,有時需要不同任務會有依賴關係,所以存在誰前誰後的問題,(關鍵字dependsOn)如a依賴於b,所以b要先執行完a才執行,如

task b{
xxx
}
task a(dependsOn b){
xxxxx
}

以上任務,a會等b運行完了再運行
也可以依賴多個任務,如a依賴b,c任務

task c{
xxx
}
task b{
xxx
}
task a{
dependsOn c,b
xxx
}

每個任務在project中都爲一個屬性,所以可以調用這個屬性如

c.doLast{
xxx
}
c.doFirst{
xxx
}

此處調用了c的兩個方法,他們的first和last時在c任務運行後的

自定義prohect屬性

使用ext來自定義屬性

ext age = 5//定義一個屬性

ext {//定義多個屬性
name = '666'
address = '你很棒'
}

定義的屬性相當於全局變量

任務分組和描述

分組即描述是爲了更好的管理這些任務
task.group = BasePlugin.BUID_GROUP//將任務分組到BUID_GROUP中
task.description = ‘測試用的’//添加任務描述,說明任務的作用

任務的執行

每個任務內都有一個List列表,保存的就是任務要執行的action,所以doFirst和doLast是對List添加action在列表的頭部或尾部,然後從頭到尾執行,這裏還有和doSelf是中間,也是任務本身.
可以用任務排序來控制任務的執行順序,聽說這個爲base版,後期可能更改
task.shouldRunAfter(task2)//task建議在task2之後執行,這個用處不大,實際運行可能會也可能不會
task.mustRunAfter(task2)//task一定在task2之後執行

任務啓用

任務中有個enabled參數,用於控制任務是否啓用,默認爲true啓用,設置爲false是不執行,並提示跳過此任務

task not{
xzxxx
}
not.enabled = false

斷言

groovy中除了可以使用if else來斷言,還提供了onlyIf,其作用於任務的條件執行,onlyIf接收一個方法塊的返回值,true則執行此任務,否則不執行

task.onlyIf{
Object build = project.property("build")
if(build.equals("666")){
true
}
false

}

以上任務開啓時使用命令傳入build參數

./gradlew -p build=666 :task

任務規則

在gradle任務執行出現錯誤,無法識別該任務或找不到時,會調用這個規則.

addRule("測試規則"){
String taskName->
task(taskName){
println "測試出現個提示"
}
}
findByName("666")//查找時,出現無此任務時會觸發規則,發出提示

gradle插件

應用二進制插件

apply plugin:a//應用a插件,這裏的a建議用全限定名

腳本插件

將腳本加載到本gradle中
build.gradle

apply from:'a'

a.gradle

ext{
version = '1.0.0'
name = 'test'
}

相當於build中就有了a中的參數

apply也可使用閉包

apply {
plugin :xxx
plugin : xxx
...
}

應用第三方插件

在應用二進制第三方插件時,需要配置buildscript中的dependencies中的classpath,指定使用的gradle版本.若此插件被官網託管了就可以不用設置.
這裏寫圖片描述

依賴第三方

compile 編譯時依賴
runtime 運行時依賴
testCompile 編譯測試時依賴,打包不會依賴進去
testRuntime 運行測試時依賴
archives 項目發佈構建(jar)依賴
default 默認依賴

也可以指定一個源集依賴
mainCompile //main源集依賴

compile project(‘:a’)//依賴a工程
compile files(‘libs/a.jar’)//依賴jar包
compile fileTree(dir:’libs’,include:’*.jar’)//依賴libs目錄下所有jar包

源集

sourceSets,用於設置資源的配置,如資源所在的路徑和編譯後的路徑
其中的屬性
name//只讀,如main
output.classesDir//指定源集編譯後的class目錄
output.resourcess//指定源集編譯後生成的資源目錄
compileClasspath//編譯指定源集時所需的classpath
java//指定源集java源文件
java.srcDirs//指定源集java源文件所在目錄
resources//指定源集的資源文件
resources.secDirs//指定源集的資源文件目錄

sourceSets{
main{
java{
scrDir 'src/java' // 設置main源集的java源文件的目錄
}
}
}

這裏寫圖片描述

這裏寫圖片描述

Android的Gradle

各屬性

compileSdkVersion//編譯的androidSDK版本,可爲int或String
BuildToosVersion//構建版本,23.0.1
defaultConfig//默認配置,配置生產模式,多渠道打包
buildTypes//配置源文件資源文件,混淆,文件目錄

defaultConfig配置

applicationId//指定生成的報名,默認爲null,會從manifest中讀取
minSdkVersion//App支持的最低Android版本
targetSdkVersion//App是基於那個android版本開發的,默認爲null,會從manifest讀取
versionCode//App內部版本,給內部人員看的
versionName//與versionCode類似,但其是給用戶看的外部版本
testApplicationId//測試App的包名,一般使用默認的,applicationId+’test’
testInstructionRunner//配置單元測試使用的Runner,默認爲android.test.InstrumentationTestRunner
proguardFile//設置一個混淆文件
proguardFiles//設置多個混淆文件
signingConfig//配置簽名信息
signingConfigs//配置多個簽名信息

android{
compileSdkVersion 23
buildToolsVersion "23.0.1"

signingConfigs{
release{//正式版
storeFile file("myreleasekey.keystore")//設置簽名文件
storePassword "password"//簽名的store密碼
keyAlias "Alias"//簽名的Alias
keyPassword "password"//簽名的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//此處也能設置簽名
}
debug{
signingConfig.signingConfigs.debug
}
}

buildTypes配置

applicationIdSuffix//配置applicationId的後綴,相當於改包名了
debuggable//配置生成的apk是否可調試
jniDebuggable//配置生成的apk是否可進行jni調試
minifyEnabled//是否啓用混淆
multiDexEnabled//是否啓用分包,方法超過65535時需要拆分多個包
proguardFile//配置混淆文件
proguardFile//配置多個混淆文件
shrinkResources//是否自動清理未使用的資源,默認false
signingConfig//配置簽名信息
testFunctionalTest//是否爲功能測試
testHandleProfiling//是否啓用分析功能
useJack//是否啓用,新的編譯器,這個編譯器更快,但是目前還不成熟

可以看到buildTypes和defaultConfig有些屬性重複了,但一般默認配置在前,然後在buildType中根據不同渠道去修改相關屬性,未設置的就是用默認配置

zipalign優化

android提供的整理優化apk的工具,可提高運行效率,降低內存使用,使用方法很簡單

android{
buildTypes{
release{
zipAlignEnabled true//啓用zipAlign
}
debug{
...
}
}
}

使用共享庫

如在6.0之後httpClient被刪除了,要使用的話需要手動添加共享庫,build.gradle中爲

android{
useLibrary('org.apache.http.legacy')
....
}

manifest.xml,這裏不寫不會出錯

<uses-library
android:name="org.apache.http.legacy"
android:required="true"

gradle中使用命令行

gradle提供了exec來執行shell命令,

def stdout = new ByteArrayOutputStream()
exec{
commandLine 'git','tag','--list'
standardOutput = stdout//獲取命令執行的返回
}
return stdout.toString()

動態設置Manifest

可使用Gradle動態替換manifest中${}佔位符
manifest.xml

<meta-data android:value="${TEST}" android:name="test"/>

在gradle中使用manifestPlaceholders來替換
gradle

productFlavors{
google{
manifestPlaceholders.put("TEST","google")
}
baidu{
manifestPlaceholders.put("TEST","baidu")
}
}

可使用遍歷所有productFlavors來替換,這樣可能沒那麼靈活,但是對於很多渠道且不通點可在productFlavors中獲取的時候,使用會很方便

productFlavors.all{
google{
}
baidu{
}
}
productFlavors.all{flavor->
manifestPlaceholders.put("TEST",name)//此處的那麼是閉包名/任務名,也就是上面的google和baidu
}

BuildConfig

BuildConfig中包含了基本版本信息,如版本號,渠道,是否爲DEBUG模式,其不可修改,而且是自動生成的

如平常獲取包名使用context.getPackageName(),這裏面實現較爲複雜,性能不高,而使用BuildConfig.APPLICATION_ID獲取到的就很方便,且其爲一個全局的靜態變量,獲取的性能高

而其中的BuildConfig.DEBUG,標記是否爲DEBUG模式,是則爲true,不是則爲false

當然也可以在構建的時候向BuildConfig加入我們自定義的參數,格式爲:
BuildConfigField ‘type’,’name’,’value’//依次爲類型,參數名,參數值

buildTypes{
release{
BuildConfigField 'String','HOST','"http://www.baidu.com"'
}
debug{
BuildConfigField 'String','HOST','"http://www.google.com"'
}
}

這樣,在正式包下,BuildConfig.HOST的值爲baidu的,測試包下爲google,這裏需要注意的是參數值爲String的需要有雙引號‘“xxxx”’,如果寫成了’xxx’,會出錯,原因是他在文件中會寫成

public static final String HOST = www.baidu.com;//這樣會出錯,因爲後面的不會被識別爲字符串
public static final String HOST = "www.baidu.com";//這樣纔是正確的

自定義資源文件的內容

一般使用的資源文件,如string.xml,color.xml等,都可以在gradle中動態修改,在buildTypes和productFlavors中都可以修改
使用resValue ‘type’,’name’,’value’//這裏和BuildConfig類似,type爲資源類型,如string,name是資源中的標識名字,value是要設置爲什麼值

productFlavors{
google{
resValue 'string','app_name','google'
}
baidu{
resvalue 'string','app_name','baidu'
}
}

上述代碼,可在不同渠道下,修改string.xml中的app_name的值

JAVA編譯選項

爲設置java編譯的JDK版本,文件編碼,在android{}中提供了compileOption來設置這些,其中只提供了三個屬性encoding(編碼格式),sourceCompatibility和targetCompatibility爲JDK版本號

android{
compileOptions{
encoding = 'utf-8'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}

adb操作配置

gradle中提供了對adb命令的配置,其中配置的屬性爲兩個timeOutInMs(超時),installOptions(安裝配置)
這個也是android{}中提供的adbOptions設置的

android{
adbOptions{
timeOutInMs = 5*1000//5秒超時
installOptions '-r','-s'//安裝配置
}
}

其中timeOutInMs爲adb命令反應超時,在指定時間內無反饋則視爲超時,報錯CommandRejectException
installOptions安裝的命令配置,
-l:鎖定該應用程序;
-r:強制安裝;
-t:允許測試包;
-s:安裝到SD卡上;
-d:允許降級安裝;-
g:授予運行時權限;

dex選項

配置dex命令運行的選項

android{
javaMaxHeapSize "2g"//調用dx命令是,分配最大的堆內存,看電腦的配置
incremental true//開啓增量模式,默認爲false
jumboMode true//開啓jumbo模式,用於突破65535方法數限制
threadCount 2//運行dx命令時的線程數
}

資源自動清理

在工程中,會應用第三方資源或有自己的代碼不使用的,所以在gradle中提供打包時不將無用資源打包的設置

shrink

android{
buildTypes{
release{
shrinkResources true//開啓自動清理,默認爲false
}
}
}

開啓很簡單,但是其清理會清理未被引用的資源,但是有些使用反射的會識別不到,所以gradle提供了keep文件來設置不清理的文件,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爲不清理文件,支持*通配符,用逗號分隔文件
shrinkMode爲清理模式,默認safe,一般用safe就好了

resConfig

resConfig可配置只需要什麼資源

android{
defaultConfig{
resConfig 'zh'
}
}

修改發佈的aar包

修改發佈的arr包

android{
defaultPublishConfig "dubug"
}

發佈多個arr包

android{
publishNonDefault true//開啓多個arr包
}
dependencies{
flavo1Compile project(path:':lib1',configuration:'flavor1Release')
flavo2Compile project(path:':lib1',configuration:'flavor2Release')
}

Junit測試

這個好像也叫單元測試,針對純java的測試
使用單元測試需要導入Junit依賴,不過一般工程都自動導入了.

dependencies {
    testImplementation 'junit:junit:4.12'
}

在java目錄下有三個包,其中有一個爲test的另兩個爲主程序和androidTest(這是android環境的測試)
test包下的就是單元測試方法的編輯
例如要測試一個自己的方法

public class TestUtil {
    public String get(){
        return "777";
    }
}

在test中新建一個類用於單元測試MyTest.java,這個代碼不會加入apk包中

import org.junit.Test;
import static org.junit.Assert.*;

public class MyTest {
    @Test
    public void test(){
        assertEquals(new TestUtil().get(),"666");
    }
}

其中@Test爲聲名此方法爲測試方法,聲名後再單元測試的時候會調用此方法進行測試
還有assert系列方法,這裏的assertEquals是判斷兩個參數是否相同,不同會有錯誤提示
在命令框使用 gradle test 進行單元測試,測試結果會在app/build/reports/tests/testDebugUnitTest/classes中已HTML的形式保存,這了一個測試類會生成一個HTML文件

測試配置

可配置測試結果數據的目錄

android{
testOptions{
reportDir = "$project.buildDir/app/report"//$project.buildDir爲工程更目錄
resultsDir = "$project.buildDir/app/result"
}
}

代碼覆蓋率

檢測測試用例的覆蓋率
需要在buildTypes中開啓testCoverageEnabled,並導入jacoco

...
apply plugin: 'jacoco'
...
android{
...
buildTypes{
debug{
testCoverageEnabled true
}
}
...
}
...
jacoco{
    toolVersion = "0.7.1.201405082137"
}

使用命令進行測試 gradle createDebugCoverageReport
結果在app\build\reports\coverage\debug\index.html中

Lint測試

檢查哪些代碼沒被使用,哪些使用新API等,生成一個報告,告訴哪裏需要優化.
在gradle中使用lintOptions進行配置

android{
lintOptions{
abortOnError true//發現錯誤是否退出構建
absolutePaths false//錯誤的輸出是否顯示絕對路徑,默認爲相對路徑
check 'NewApi'//設置需要檢測哪些Lint檢查,具體項目使用命令查看lint --list
checkAllWarning true//是否檢測所有警告的issue
checkReleaseBuilds true//在release中是否檢測致命錯誤,出現錯誤終止構建
disable 'NewApi'//關閉哪些issue檢查
enable 'NewApi'//開啓哪些檢查
explainIssues true//錯誤報告是否包含解釋說明
htmlOutput new File("xxx")//配置Html報告的輸出路徑
htmlReport true//是否生成html報告
ignoreWarnings false//是否忽略警告級別的檢查
lintConfig new File("xxx")//指定Lint的配置文件,一個Xml文件
noLines true//錯誤信息中,是否不包含源代碼中的行號
quiet false//是否安靜模式,安靜模式不會顯示分析進度
severityOverrides//返回一個Map結果,內容爲個issue的優先級
showAll true//是否顯示全部輸出,不爲true,較長的信息會被截斷
textOutput new File("xxx")//生成text報告的路徑
textReport false//是否生成text報告
warningAsErrors false//所有警告是否當成錯誤處理
xmlOutput new File("xxx")//xml報告輸出路徑
xmlReport true//是否生成xml報告


}
}

本筆記是學習《Android Gradle 權威指南》作的筆記,當中有些內容可能錯誤或描述不清晰的,敬請諒解~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章