系列目錄
1.【Gradle深入淺出】——初識Gradle
2.【Gradle深入淺出】——Gradle基礎概念
3.【Gradle深入淺出】——Android Gradle Plugin 基礎概念
4.【Gradle深入淺出】——Gradle配置(一)
5.【Gradle深入淺出】——Gralde配置(二)
前言
前面一篇博客講解了Gradle的基本概念,里面也提到Gradle的實質其實是一個流程控制框架,本身是沒有業(yè)務邏輯的,而其本身的強大之處在于語法糖還有版本控制還有插件支持,這個就給復雜的工程結構構建提供了靈活的控制方式。所以這篇博客就針對我們經常使用的AGP來進行一個基礎概念講解。
首先來看下什么是AGP,全稱Android Gradle Plugin也就是Google針對Android項目的構建專門開發(fā)的一個Gradle插件。
Configurations
這里首先講的是Configurations,為什么要講這個呢,因為這個概念其實在后面我們Android工程的編譯構建發(fā)現(xiàn)是一個非常重要的概念,那就是依賴管理。
前面一篇博客我們簡單的介紹來依賴的概念,依賴分為repositories和dependencies,分別表示依賴庫的下載地址和具體的依賴庫。這里我們先考慮一種場景,比如我們需要在編譯時使用一組依賴A,在測試時需要依賴一組依賴B用于測試,這時候就會發(fā)現(xiàn)我們肯定需要一個字段或者一個策略來區(qū)分這兩組依賴,所以Gradle就提出了configuration的概念,這里面每一組依賴稱為一個Configuration。我們首先來看下Configuration如何定義。
//方式一
configurations {
testDenp
}
//方式二
configurations.create('testDenp')
這樣我們就定義了一個名為testDenp的Configuration,這樣我們在聲明依賴的時候,就可以通過定義不同的Configuration來區(qū)分不同條件下的依賴關系。那么我們來看下如何使用我們定義的Configuration。
dependencies {
testDenp 'androidx.appcompat:appcompat:1.0.2'
}
這樣我們就講一個庫加入我們的testDenp組中了,后面我們在構建的時候就可以按照我們地方需求用不同組的依賴來進行構建了。
這里我們列舉下常用的幾個configuraion,如compile,runtime之類已經默認被加入到java插件中了,而androidTestCompile,implementation這類就是AGP中默認添加的Configuration。
這里其實我挺想吐槽一下這里的命名的,起初我一直搞不明白configuraion的含義,我感覺如果叫做denpendencyGroup之類的會更讓人容易理解些。
BuildType
字如其名,BuildType表示的就是構建類型,我們常說的release和debug就是兩個BuildType,AGP默認會給項目增加Debug和Release兩個BuildType,并且默認配置了一套默認值,例如Debug中的debuggable默認為true,Release的debuggable默認為false.
Build types(構建類型)定義了一些構建、打包應用時 Gradle 要用到的屬性,主要用于不同開發(fā)階段的配置,如 debug 構建類型要啟用 debug options 并用 debug key 簽名,release 構建類型要刪除無效資源、混淆源碼以及用 release key 簽名
而我們想要自己定義一套我們的buildType如何定義呢?
android {
...
defaultConfig {...}
buildTypes {
release {
//開啟混淆
minifyEnabled true
//混淆規(guī)則文件
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
//apk的后綴
applicationIdSuffix ".debug"
}
//debug的一個擴展
develop {
// 復制debug的屬性和簽名配置
initWith debug
applicationIdSuffix ".develop"
}
}
}
只需要在android{}中的buildTypes{}中增加一種類型即可。這里有幾點需要注意:
- 配置buildTypes之前,必須要配置signingConfigs,把簽名文件配好,否則就會出現(xiàn)installTask缺失的情況。
- 我們可以使用
initWith方法來繼承一個配置,可以理解成拷貝了debug這一構建類型的所有變量,因為我們知道,每一個構建類型都有一些默認的變量,例如debuggable、zipAlignEnabled等,使用該配置就免去為新增的構建類型定義所有的變量。
我們每新建一個BuildType都會創(chuàng)建一個新的assemble任務,assembleDebug和assembleRelease兩個Task在上面已經提到過,這里就會新增一個assembleDevelop這樣的任務,我們通過./gradlew assembleDevelop就會按照這個BuildType來打包。必須在 app 模塊下的 build.gradle 中定義新的構建類型,gradle 才會生成新的task。
而BuildType中常用的定義的屬性有
| 屬性 | 描述 |
|---|---|
| boolean debbuggable | 該構建類型是否生成一個可調式的apk |
| boolean minifyEnabled | 是否可以移出無用的java代碼,默認為false |
| Boolean multiDexEnabled | 是否可以分包 |
| File multiDexKeepFile | 指定放在main dex內的類,如果設置則它的格式為一個類一行:com/example/MyClass.class |
| File multiDexKeepProguard | 指定用在main dex 的類上的混淆文件,跟系統(tǒng)混淆文件聯(lián)合使用 |
| String name | 這種構建類型的名稱 |
| proguardFiles | 指定插件使用的混淆文件 |
| SigningConfig signingConfig | 簽名配置文件 |
| boolean zipAlignEnabled | 是否使用zipAlign優(yōu)化apk,Android sdk包里面的工具,能夠對打包的應用程序進行優(yōu)化,讓整個系統(tǒng)運行的更快 |
| String versionNameSuffix | VersionName的后綴 |
ProductFlavor
和上面提到的BuildType一樣,ProductFlavor也是用于區(qū)分一種構建場景的設置,只不過維度不一樣,這里指的是構建渠道,我們在打包發(fā)布的時候可能經常有這樣的需求,不同的市場渠道號是不一樣的,這樣我們就可以定義不同的Flavor,然后進行配置。
android {
productFlavors {
kuan {}
xiaomi {}
qh360 {}
baidu {}
wandoujia {}
}
productFlavors.all {
flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}
}
也可以通過productFlavors.all 的代碼所有 flavor 都會執(zhí)行。
flavor會和buildType做一個組合,生成編譯task
如果增加了flavor名為 dev, 會新增assembleDevRelease 和assembleDevDebug 命令。
所以結合上面的BuildType,我們就可以組合出例如小米市場Debug包、官方市場Release包。
并且我們每創(chuàng)建一個Flavors,對應的依賴也可以生成對應的依賴關系,例如xiaomiCompile。
BuildVariant
上面介紹了BuildType和ProductFlavor,那么這里說的BuildVariant是什么呢?這里放一個公式立馬就清晰了許多。
BuildVariant = ProductFlavor x BuildType
也就是我們剛才上面說的BuildType和ProductFlavor的組合,最終來構建產物,我們Studio面板的左邊的側邊欄就有對應的BuildVariant選項,用于我們來進行構建選擇。

例如如下的配置:
productFlavors {
pro {
}
fre {
}
}
buildTypes {
debug {
}
release {
}
}
這兩個維度的組合,會產生如下包:
- proDebug
- proRelease
- freDebug
- proRelease
這里有幾個地方需要注意下: - 1.buildTypes不能設置 applicationId
- 2.productFlavors不能設置 minifyEnabled,如果需要同時設置混淆和applicationId,需要flavor和buildType組合
BuildConfig
BuildConfig是android studio在打包時自動生成的一個java類。我們可以理解是我們在構建時生成的一個Java靜態(tài)類,我們可以在這個類里存放打包時的相關常量,例如IS_DEBUG這樣的字段,用于判斷我們構建的包是不是debug包。
BuildConfig類在項目工程的build/generated/source/buildConfig/androidTest或debug或release中,這些目錄中的BuildConfig類中有相同的常量字段。
BuildConfig的生成
defaultConfig {
...
// 自定義的方法就是 buildConfigField ,這種是groovy寫法
// 三個參數(shù)分別是 type (類型) , name (命名) , value(值)
buildConfigField 'int' , 'SEVER_CONFIG' , "1"
// 當然寫成這種更容易看懂,這種寫法更像java。
// 三個參數(shù)分別是 type (類型) , name (命名) , value(值)
buildConfigField("int" , "SEVER_CONFIG" , "1")
}
就像上面舉例一樣,然后我們使用buildConfigField方法,然后rebuild一下,我們就會在剛才的提到的那個目錄看到生成的BuildConfig類。
這里要注意下, "1" 對應的是1,如果是String加上"號,所以是"\"1\""
而可以配置BuildConfig的地方有很多,那么優(yōu)先級是什么樣的呢?
按優(yōu)先級從高到低: buildType->Flavor->defaultConfig
例如同時存在同一變量定義.
productFlavors {
dev {
buildConfigField "String", "ADD_BY_FLAVOR_DEV", "\"set_in_flavor\""
}
}
defaultConfig {
buildConfigField "String", "ADD_BY_FLAVOR_DEV", "\"set_in_default_config\""
}
buildTypes {
debug {
buildConfigField "String", "ADD_BY_FLAVOR_DEV", "\"set_in_build_type\""
}
}
最終 buildTypes的會生效.
而且這個優(yōu)先級關系在很多場景下都有效,例如Manifest文件沖突,后續(xù)專門開一篇博客講解吧。
SourceSet
java插件引入了一個概念叫做SourceSets,通過修改SourceSets中的屬性,可以指定哪些源文件(或文件夾下的源文件)要被編譯,哪些源文件要被排除。Gradle就是通過它實現(xiàn)Java項目的布局定義。
SourceSet 可以定義項目結構,也可以修改項目結構。Java插件默認實現(xiàn)了兩個SourceSet,main 和 test。每個 SourceSet 都提供了一系列的屬性,通過這些屬性,可以定義該 SourceSet 所包含的源文件。比如,java.srcDirs,resources.srcDirs 。Java 插件中定義的其他任務,就根據(jù) main 和 test 的這兩個 SourceSet 的定義來尋找產品代碼和測試代碼等。
例如我們定義我們項目的Java文件的目錄。
android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
官方對于SourceSets的配置項的介紹:

當然SourceSets也可以指定多個路徑,比如如果我們指定我們的Java路徑是src/main.java我們在這個目錄下使用Studio創(chuàng)建Java文件,就會發(fā)現(xiàn)有Javb Class的選項

但如果我們這時候在src目錄下新建一個src/test目錄,我們再使用Studio創(chuàng)建文件,就會發(fā)現(xiàn)沒有Java Class的選項,這就是因為我們配置來SourceSets,所以我們需要在Sourcesets中增加一個路徑。

android {
//第一種寫法
sourceSets {
main {
java {
srcDir 'src/main/java'
srcDir 'src/test' //指定 test 為源碼目錄
}
}
}
}
//第二種寫法
android {
sourceSets {
main {
java.srcDirs( 'src/main/java' , 'src/test' )
}
}
}
這樣我們重新build一下,就會發(fā)現(xiàn)可以在這個目錄下新建Java文件。
總結
這篇博客從AGP的角度來看了下Android中Gradle的相關配置項的基礎概念,支持通過前三篇博客我們應該對于Gradle有了一個基本的掌握和理解,接下來我們就開始深入Gradle,開始我們的Gradle的學習旅程吧。