1. 構建文件
在Android構建項目時會自動生成三個gradle文件:setting.gradle和build.gradle以及Android模塊內的build.gradle文件。
1.1 settings文件
settings文件在初始化階段被執行,并且定義了哪些模塊應該包含在構建內,Gradle會為每個settings文件創建一個Settings對象。默認情況下的內容如下:
include ':app'
在上述代碼中,只有app模塊被包含在內。正常來講,單模塊構建不一定需要settings文件,但是多模塊構建一定需要,否則Gradle不知道哪個模塊要包含在構建內。
1.2 頂層構建文件
頂層構建文件即位于根目錄下的build.gradle文件。所有模塊的配置參數都是在頂層的build.gradle文件中配置的。默認配置如下:
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
實際的構建配置都在buildscript代碼塊內。
repositories代碼塊主要用于配置項目依賴倉庫,上述代碼將google,jcenter兩個倉庫配置成倉庫,配置過后我們的應用或者依賴項目就可以下載對應倉庫下的一系列依賴包。
dependsncies代碼塊用于配置構建過程中的依賴包。值得注意的是不能將應用或者依賴項目所需要的依賴包包含于頂層構建文件中,默認情況下,唯一被定義的依賴包是Gradle的Android插件。每個Android模塊都需要有Android插件才可以執行Android相關任務。
allprojects代碼塊可用來聲明那些需要被用于所有模塊的屬性。
1.3 模塊的構建文件
模塊內部的build.gardle文件的屬性只能應用在Android app模塊,它可以覆蓋頂層build.gradle文件的任何屬性。默認情況下如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.example.liubohua.gradletest"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
}
總體大致可分為三個部分:插件依賴、Android以及依賴包。
插件依賴
第一行用到了Android應用插件,在頂層構建文件中被配置成了依賴。
Android
引入插件之后,需要對Android進行配置,配置的基本信息如下:
- compileSdkVersion為必須要有的屬性,用來編譯應用的Android API版本。
- defaultConfig代碼塊用于配置應用的核心屬性。此段代碼中的屬性可以覆蓋AndroidManifest.xml中的屬性。
defaultConfig {
applicationId "com.example.liubohua.gradletest"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
- applicationId覆蓋了manifest文件中得到的packagename。
- minSdkVersion被用來配置運行應用的最小API級別,targetSdkVersion用于通知系統,該應用已經在某特定Android版本通過測試,從而操作系統不必啟用任何向前兼容的行為。兩種屬性對應manifest的<uses-dsk>中的屬性。
- versionCode與versitionName表示為你的應用定義一個版本號和版本名稱。同樣會覆蓋manifest當中的屬性。
- builddTypes代碼塊用來定義如何構建和打包不同構建類型的應用。
依賴包
依賴代碼塊定義了一個應用或依賴項目的所有依賴包。默認情況下會對libs文件夾下的所有jar文件構成依賴。
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
2. 自定義構建
Gradle構建項目時,可以生成一個BuildGradle類,使用buildConfigField和resValue可以對這個類里面添加自定義的常量,使得在不同的構造模式下會產生不同的效果:
buildTypes {
debug {
minifyEnabled false
buildConfigField("String","TEST_URL","\"http://www.baidu.com\"")
resValue("string","test_res","debug_res")
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
buildConfigField("String","TEST_URL","\"http://www.google.com.hk\"")
resValue("string","test_res","release_res")
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
這里我們在debug模式和release模式下分別定義了test_res和TEST_URL的不通值,使用在java代碼中的引用如下:
//debug模式為http://www.baidu.com,release為http://www.google.com.hk
BuildConfig.TEST_URL
//debug模式為debug_res,release為release_res
getResources().getString(R.string.test_res)
對于Project對象我們也可以添加屬性,添加額外屬性需要用到ext代碼塊。
如我們想要定義一個config.build文件來統一管理我們各個moudle的配置,config.gradle的內存如下:
ext {
compileSdkVersion = 26
buildToolsVersion = "26.0.2"
}
在頂層的build.gradle當中添加依賴
apply from: 'config.gradle'
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
在模塊構建文件當中引用
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
}
如果不采用config.gradle當中的配置,在模塊構建文件當中可以對屬性重新定義,如果模塊構建文件中定義的屬性在頂層構建文件中已經存在,那么新屬性將覆蓋原來的屬性。
3. 依賴配置
gradle添加依賴的方式主要有一下幾種:
- compile(implementation或api)
- apk
- provided
- testCompile
- androidTestCompile
compile是默認的配置,在編譯主應用時包含所有的依賴。該配置不僅會將依賴添加至類路徑,還會生成對應的APK。
在Android studio 3.0.0中使用了最新的Gralde 4.0 里程碑版本作為gradle的編譯版本。在新版本中compile 指令被標注為過時方法,而新增了兩個依賴指令,一個是implement 和api,兩個新指令用法相同,相比于以前的compile指令,implement指令對于使用了該命令編譯的依賴,對該項目有依賴的項目將無法訪問到使用該命令編譯的依賴中的任何程序,也就是將該依賴隱藏在內部,而不對外部公開。
apk配置使該依賴只會打包到APK,而不會添加到編譯類路徑。provided配置則完全相反,不會被打包到APK。這兩個配置只適用于JAR依賴,如果在依賴項目中添加他們則會導致錯誤。
testCompile和androidTestCompile配置會添加用于測試的額外依賴庫,在運行測試相關任務時會被使用。
除去標準配置外,Android插件還可以針對每個構建變體生成一份配置,其格式如下:buildType+配置類型,例如debugCompile、releaseProvided等。
4. 構建類型
在gradle的Android插件中,每一個模塊都有一個debug構建類型,其被設置為默認構建類型。同時我們可以在buildTypes當中自定義構建類型。
buildTypes {
debug {
minifyEnabled false
buildConfigField("String", "TEST_URL", "\"http://www.baidu.com\"")
resValue("string", "test_res", "debug_res")
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
lbh {
minifyEnabled true
applicationIdSuffix ".lbh"
versionNameSuffix "-lbh"
buildConfigField "String", "SYAGINHG_URL", "\"http://www.lbh.com\""
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
我們新定義了lbh構建類型,并為其添加設置(applicationId添加“.lbh”后綴,為versitionName版本名添加-lbh后綴)。這樣在同一個設備當中就可以同事安裝lbh版本和debug兩個版本,從而避免發生沖突。
我們也可以使用已經定義好的構建類型來初始化一個新的構建類型:
buildTypes {
debug {
minifyEnabled false
buildConfigField("String", "TEST_URL", "\"http://www.baidu.com\"")
resValue("string", "test_res", "debug_res")
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
lbh.initWith(buildTypes.debug)
lbh {
minifyEnabled true
applicationIdSuffix ".lbh"
versionNameSuffix "-lbh"
buildConfigField "String", "SYAGINHG_URL", "\"http://www.lbh.com\""
}
}
initWith()方法復制了一個已存在的構建類型的所有屬性到新的構建類型當中,當然我們可以根據需要在新的構建類型中對他們重新定義或者添加新的屬性。
5. product flavor
當我們同一個APP需要產出不同版本的應用時,就需要用到product flavor。最典型的例子是一個應用的付費版和免費版。
使用productFlavor代碼塊中添加新的Product flavor來創建。
andrlod{
flavorDimensions "color","price"
productFlavors {
red {
dimension "color"
applicationId 'com.example.liubohua.red'
versionCode 3
}
blue {
dimension "color"
applicationId 'com.example.liubohua.blue'
versionCode 4
}
free {
dimension "price"
}
paid {
dimension "price"
}
}
}
product flavor可以擁有自己的源集目錄,該文件的名稱格式為flavor名稱+構建類型名稱。
flavorDimensions字段可以為flavor添加維度,flavorDimensions后面參數的順序將決定構建variant的名稱,第一個字段將覆蓋第二個字段。假設構建類型為debug和release,則上述flavor將會生成如下variant:
- blueFreeDebug和blueFreeRelease
- bluePaidDebug和bluePaidRelease
- redFreeDebug和redFreeRelease
- redPaidDebug和redPaidRelease
6. Groovy
6.1 簡介
Groovy是一種基于JVM(Java虛擬機)的敏捷開發語言,它結合了Python、Ruby和Smalltalk的許多強大的特性,Groovy 代碼能夠與 Java 代碼很好地結合,也能用于擴展現有代碼。由于其運行在 JVM 上的特性,Groovy 可以使用其他 Java 語言編寫的庫。
簡單的配置方法如下(Mac)
- 通過官方網站下載壓縮包并解壓 http://www.groovy-lang.org/download.html
- 配置環境變量
vim ~/.bash_profile
在彈框中輸入
export PATH=$PATH:/Users/xuhongchuan/install/groovy-2.4.13/bin
退出保存。
- 讓修改的設置生效
source ~/.bash_profile
- 打印版本號確認是否安裝成功
groovy -v
打印的結果為
Groovy Version: 2.4.13 JVM: 1.8.0_141 Vendor: Oracle Corporation OS: Mac OS X
則表示安裝成功。
- 使用IntelliJ IDEA即可進行開發。
6.2 基本語法
先來看一個簡單的例子
def x = 10;
def s1 = 'hello ${x} world'
println s1
def s2 = "hello ${x} world"
println s2
結果輸出
hello ${x} world
hello 10 world
6.3 定義變量和方法
在groovy中支持動態類型,即不需要指定變量的類型。變量定義可以使用關鍵字def。
def a = 1 //定義一個整形
def b = 'dfdsfa' //定義一個字符串
def double c = 1.1 //定義一個double類型,double可以省略
無返回類型的函數定義,必須使用def關鍵字 ,最后一行代碼的執行結果就是本函數的返回值。
def func(){
return 8
}
int func2(def a,def b){
a*b
}
println func() //8
println func2(2,3) //6
定義方法時,return可以省略,如果不使用return,則默認最后一行為返回值。 返回類型可以指定為整形,也可用def,使用def返回類型與最后一行類型相同。
另一種定義方法的方式為:
def func = {num,num2 ->
num = num+num2 //num=3
num*num2
}
println(func(1,2)) //6
這種定義方式在Gradle當中很常見。這不是一個常規方法,而是一個closure,即匿名代碼塊,可以接受參數和返回值。
在這種定義中內含了未分類參數it的概念。如果你沒有明確給closure指定參數,則Groovy會自動添加一個參數it,使用方式如下
def func = {
it * it
}
println(func(8)) //64
6.4 類
在Groovy中定義一個類的方式如下:
class GroovyClass {
def str
def getStr() {
return str
}
void setStr(str) {
this.str = str
}
}
定義方式與java中的定義方式類似,但是在沒有修飾符修飾的情況下。Groovy中的默認訪問修飾符與Java不同。類和方法一樣是公有的,但是成員變量是私有的。GroovyClass的使用方式與java類似。
def testClass = new GroovyClass()
testClass.setStr('hello world')
println(testClass.str)//hello world
println(testClass.getStr()) //hello world
當你調用一個變量時,實際調用的是該變量的getter方法,所以后兩種方式打印的結果完全相同。
6.5 Groovy容器
這里介紹兩種Groovy容器:List和Map
- List:其底層對應Java中的List接口。
List list = [1,2,3,4]
println list[0]
list[2] = 5
list<<6
list.each {elements->
println(elements)
}
上述代碼初始化了一個List,對list[0]單獨進行輸出之后對list[2]進行賦值,通過“list<< 6”語句向list集合中添加元素,之后對List進行遍歷并對元素進行輸出。
輸出結果為
1
1
2
5
4
6
遍歷時也可以使用it變量。
List list = [1,2,3,4];
list.each {
println(it)
}
- map:鍵-值表
def map = [key1: "value1", key2: "value2", key3: "value3"]
println map
println(map.keySet())
println(map.values())
println("key1的值:" + map.key1)
println("key1的值:" + map.get("key1"))
map.put("key4", "value4")
Iterator it = map.iterator()
while (it.hasNext()) {
println "遍歷map: " + it.next()
}
- 定義一個map集合
- 輸出map集合
- 輸出map集合的key所組成的集合
- 輸出所有value值得集合
- 輸出集合中key1所對應的值
- 輸出集合中key1所對應的值
- 對key4進行賦值并加入map集合當中
- 遍歷輸出map集合
上述代碼輸出如下
[key1:value1, key2:value2, key3:value3]
[key1, key2, key3]
[value1, value2, value3]
key1的值:value1
key1的值:value1
遍歷map: key1=value1
遍歷map: key2=value2
遍歷map: key3=value3
遍歷map: key4=value4
7. 任務
Gradle在構建過程中一共可以分為三個階段:初始化階段、配置階段和執行階段。當我們使用task創建完一個任務時,需要添加“<<”告知Gradle代碼塊會在執行階段執行,而不是配置階段。
task hello << {
println('hello world')
}
輸出結果為:
> Task :hello
hello world
BUILD SUCCESSFUL in 1s
實際上想要在執行階段執行代碼,需要使用doFirst()或者doLast()來為一個task添加代碼。我們之前用來定義task說的左移運算符(<<)就是doFirst()得縮寫。
task hello {
println('hello world')
doFirst {
println 'Hello'
}
doLast {
println 'hello again'
}
doLast {
println 'Goodbye'
}
}
輸出結果
> Configure project :
hello world
> Task :hello
Hello
hello again
Goodbye
值得注意得是doFirst必須總是第一個添加到task當中,后續的doLast會依次順序執行。
還有兩個方法在構建時會經常用到。mustRunAfter()以及dependsOn()
- mustRunAfter():用于對task進行排序,當定義該方法后,如果涉及到的兩個任務都被執行,無論定義順序如何,執行順序都會按照之前定義好的執行。
task task1 << {
println 'test1'
}
task task2 << {
println 'test2'
}
task2.mustRunAfter task
執行./gradlew task1 task2或者./gradlew task2 task1輸出結果相同
> Task :task1
test1
> Task :task2
test2
- dependsOn():定義一個任務必須依賴于另一個任務。
task task1 << {
println 'test1'
}
task task2 << {
println 'test2'
}
task2.dependsOn task1
執行./gradlew task2直接輸出結果
> Task :task1
test1
> Task :task2
test2
相比于mustRunAfter,在未聲明task1的時候執行task2也會出發task1。
8 尾聲
剛剛接觸簡書,本人是17年畢業的萌新,最近學習了一點關于gardle的知識寫筆記記錄一下,如果有什么錯誤的地方還請大神多多指正。