Gradle系列《二》: 在Android中的應用
Gradle的更多用法
這裡主要介紹app/build.gradle這個檔案,因為這裡面的配置是與我們應用開發最為息息相關的。首先,先看看我們都可以在這個檔案中配置哪些內容(功能)。另外,這裡略去了部分上文提到的一些配置:
android {
defaultConfig {
//預設配置項
}
buildTypes {
// 編譯配置,release或debug版本的內容
}
compileOptions {
// Java 的版本配置
}
sourceSets {
//原始碼設定(專案目錄結構的設定)
}
packagingOptions {
//打包時的相關配置
}
lintOptions {
//編譯的 lint 開關,程式在buid的時候,會執行lint檢查,有任何的錯誤或者警告提示,都會終止構建,我們可以將其關掉。
//abortOnError false
}
productFlavors {
//產品釋出的一些東西,比如渠道、包名等
flavor1 {
}
flavor2 {
}
}
signingConfigs {
//簽名的配置
release {
}
}
}
接下來將對部分功能特性(主要是與Android開發相關的特性)及使用進行詳細介紹
- buildTypes–編譯配置
這裡主要是對release與debug版本做些不同的配置,如是否啟用混淆、修改對應版本的包名、對應版本使用的簽名等(更多配置可以檢視:Build Types(構建型別))。
buildTypes {
release {
//為釋出版本啟用混淆
minifyEnabled true
//混淆檔案
proguardFiles getDefaultProguardFile('proguard-android.txt' ), 'proguard-rules.pro'
}
debug {
//將debug版本的包名設定為.debug以便能夠同時在一臺裝置上安裝debug和release版本的apk。
applicationIdSuffix ".debug"
}
}
- compileOptions–配置JDK版本
compileOptions {
//配置使用JDK1.8(8),比如需要使用Lambda特性時,那麼就可以在這裡進行JDK版本的配置
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
- sourceSets
配置專案的目錄結構,其中較為常見的一個應用場景是,將Eclipse 中的專案遷移至AS,由於兩者目錄結構相差較大,所以需要手動指定,部分程式碼如:
( 更多介紹可以檢視:工程結構)
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']
}
}
- packagingOptions –打包時的相關配置
當專案中依賴的第三方庫越來越多時,有可能會出現兩個依賴庫中存在同一個(名稱)檔案。如果這樣,Gradle在打包時就會提示錯誤(警告)。那麼就可以根據提示,然後使用以下方法將重複的檔案剔除。
packagingOptions {
//這個是在同時使用butterknife、dagger2做的一個處理。同理,遇到類似的問題,只要根據gradle的提示,做類似處理即可。
exclude 'META-INF/services/javax.annotation.processing.Processor'
}
- productFlavors
這個配置是經常會使用到的,通常在適配多個渠道的時候,需要為特定的渠道做部分特殊的處理,比如設定不同的包名、應用名等。場景:當我們使用友盟統計時,通常需要設定一個渠道ID,那麼我們就可以利用productFlavors來生成對應渠道資訊的包,如:
android {
productFlavors {
wandoujia {
//豌豆莢渠道包配置
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
//manifestPlaceholders的使用在後續章節(AndroidManifest裡的佔位符)中介紹
}
xiaomi {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"]
applicationId "com.wiky.gradle.xiaomi" //配置包名
}
_360 {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "_360"]
}
//...
}
}
當然也有更簡潔的方式:
android {
productFlavors {
wandoujia {}
xiaomi {}
_360 {}
//...
}
productFlavors.all {
//批量修改,類似一個循序遍歷
flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}
}
配置完之後,在命令列視窗中(Terminal)中輸入gradlew assembleRelease(window)即可開始打包,在Mac系統中對應指令應該是./gradlew assembleRelease。當然,如果想要debug版本的包,將指令中assembleRelease改為assembleDebug即可。最後生成的包還是在app/build/outputs/apk中,預設命名格式如app-wandoujia-release-unsigned.apk。
- signingConfigs–應用簽名配置
為release版本配置簽名,如:
android {
signingConfigs {
release {
//簽名證書檔案
storeFile file("release.keystore")
//別名
keyAlias "release"
//key的密碼
keyPassword "123456"
//證書的密碼
storePassword "123456"
}
debug {
}
}
//需要注意的是:配置好籤名資訊後,需要在buildTypes配置使用
buildTypes {
release {
signingConfig signingConfigs.release
}
debug {
}
}
}
當然,應用簽名可以借用AS(build – > generate signed apk)提供的圖形化介面來完成,如:
具體設定流程就不再這裡介紹了。
批量修改生成的apk檔名
場景:我們需要一次性打多個包並且想讓生成的apk檔名有區分。比如格式為:應用名版本打包時間_渠道.apk。那麼我們可以採用如下方式:
//獲取並格式化時間資訊,注意,應該是與android{}同一層級
def buildTime() {
return new Date().format("yyyy_MM_dd", TimeZone.getTimeZone("UTC"))
}
android {
buildTypes {
release {
//只針對release版本進行配置
applicationVariants.all {
variant ->
variant.outputs.each {
output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk') && 'release'.equals(variant.buildType.name)) {
// 輸出apk格式命名如:Gradle_v1.0_2016_07_01_wandoujia.apk
def fileName = "Gradl_v${variant.versionName}_${buildTime()}_${variant.flavorName}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}
}
}
AndroidManifest裡的佔位符
AndroidManifest.xml就不在這裡介紹了。我們知道,專案中的很多配置都需要在這裡定義。而當在需要生成多個版本並要為每個版本配置相應的屬性值時,以我們最基本的做法(每生成一個版本前修改對應的屬性值)來看,這將是一件麻煩的事情。現在Gradle為我們提供了一個解決方案,就是利用manifestPlaceholders,進行動態賦值。它的使用有點類似HashMap。現在還是以使用友盟統計時的配置舉例:在友盟統計分析中,我們需要在AndroidManifest裡配置一個name為UMENG_CHANNEL的meta-data,這樣這個meta-data的值就表示這個apk是哪個渠道,如配置小米應用商店:
<meta-data android:value="xiaomi" android:name="UMENG_CHANNEL"/>
顯然,這個value值,不同版本的包都需要不同,所以以下為了能夠使用manifestPlaceholders進行動態替換,我們需要換一種寫法:
<meta-data android:value="${UMENG_CHANNEL_VALUE}" android:name="UMENG_CHANNEL"/>
如上${UMENG_CHANNEL_VALUE}就是一個佔位符,然後我們在gradle的defaultConfig;裡這樣定義指令碼:
android {
defaultConfig {
//可以在預設配置中設定,以下的語法格式應該比較清晰,就不再介紹了。
manifestPlaceholders = [UMENG_CHANNEL_VALUE: 'xiaomi']
}
}
我們回顧一下上文中介紹productFlavors時,對每個渠道包進行配置時所舉的例子正是利用manifestPlaceholders配置UMENG_CHANNEL_VALUE的值,如:
android {
productFlavors {
wandoujia {
//豌豆莢渠道包配置
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
//另外寫法也有manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'xiaomi'),稍有區別。
}
//...
}
}
通過以上例子,我們可以發現,AndroidManifest中的一些屬性值也是可以動態改變的,這樣在對特定包進行特定處理也將變得十分方便,更多的用法依次類推即可。
自定義你的BuildConfig
BuildConfig.java是Android Gradle自動生成的一個java類檔案,無法手動編譯,但是可以通過Gradle控制,也就是說他是動態可配置的。比如在build.gradle中配置buildConfigField “boolean”, “LOG_DEBUG”, “false”後,我們點選“Sync now”後,即可在程式碼中使用BuildConfig.java新加的LOG_DEBUG屬性,如:
public final class BuildConfig {
//...
// Fields from build type: debug
public static final boolean LOG_DEBUG = true;
}
buildConfigField 一共有3個引數,第一個是資料型別,即新增成員變數的型別(與Java的型別是對等);第二個引數是成員變數名,如:LOG_DEBUG;第三個引數是常量值。
以下我們介紹一種常見的用法,場景:我們需要在debug版本包中輸出log,而不希望在release版本中輸出log,那麼就可以通過以下方法進行配置(當然,我們程式碼中的log開關是使用BuildConfig中相關屬性進行設定的):
buildTypes {
debug {
// 顯示Log
buildConfigField "boolean", "LOG_DEBUG", "true"
//...
}
release {
// 不顯示Log
buildConfigField "boolean", "LOG_DEBUG", "false"
//...
}
}
另外,還用一種常見的情況是:當我們的伺服器有debug和release環境時,我們可能也需要在App對應的版本中配置對應的資訊。如在debug版本中配置服務端debug環境對應的url。這樣我們在開發的過程中將減少很多麻煩的工作。具體做法就不再細說了。
PS:buildConfigField也是可以在對特定渠道包處理時進行特定的配置。