有時候我們一個app需要有不同的版本,不同的版本又會使用不同的配置,我們可以使用gradle進行管理。
- Build types
- Product flavors
- Build variants
- Signing configurations
一、構建版本Build types:
常見的構建版本有debug與release。
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
自定義構建版本:
custom {
applicationIdSuffix ".custom"
versionNameSuffix ".custom"
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.readerDaquanConfig
}
除了debug構建版本不需要簽名外,其它的都是需要配置簽名的,不然無法執行在手機上,該版本定義了新的applicationId與版本號。不同構建版本的applicationId如下:
- Debug: com.package
- Release: com.package
- Staging: com.package.staging
也可以採用繼承的方式:
custom.initWith(buildTypes.debug)
custom {
applicationIdSuffix ".custom"
versionNameSuffix ".custom"
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.readerDaquanConfig
}
custom繼承debug構建版本的配置,custom中的配置會覆蓋debug的配置。
在BuildConfig中新增變數
custom {
applicationIdSuffix ".custom"
versionNameSuffix ".custom"
buildConfigField("String", "name","\"custom app\"")
buildConfigField("int", "id", "0")
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.readerDaquanConfig
}
之後我們可以看到:
注意如果要新增的是String,那麼雙一號裡面需要再一個雙引號標識字串"\"custom app\"",斜杆是對裡面雙引號進行轉義。
Source sets
source set
。預設的source set目錄會放在相同的Build Type的目錄下。當你建立一個新的build type時,該目錄不會自動建立,你必須在你使用程式碼與資源前自己為每一個build type建立source set目錄。app
└── src
├── debug
│ ├── java
│ │ └── com.package
│ ├── res
│ │ └── layout
│ │ └── activity_main.xml
│ └── AndroidManifest.xml
├── main
│ ├── java
│ │ └── com.package
│ ├── res
└── MainActivity.java
└── Constants.java
│ └── AndroidManifest.xml
├── custom
│ ├── java
│ │ └── com.package
├── drawable
└── layout
└── activity_main.xml
│ ├── res
│ │ └── layout
│ │ └── activity_main.xml
│ └── AndroidManifest.xml
└── release
├── java
│ └── com.package
│ └── Constants.java
└── AndroidManifest.xml
假如我們自己建立custom的source set
我們使用不同的構建版本便會使用不同的source set。當使用不同的source sets的時候,資原始檔的處理需要特殊的方式。Drawables和layout檔案將會複寫在main中的重名檔案,但是values檔案下的資源不會。gradle將會把這些資源連同main裡面的資源一起合併。(如果出現資源重複異常,請clean一下工程)
例如,在main中的string.xml為:
<resources>
<string name="app_name">BuildTypeProject</string>
<string name="hello_name">BuildTypeHello</string>
</resources>
在custom版本中為:
<resources>
<string name="app_name">BuildTypeCustomProject</string>
</resources>
當我們構建custom版本的時會合併為:
<resources>
<string name="app_name">BuildTypeCustomProject</string>
<string name="hello_name">BuildTypeHello</string>
</resources>
當你建立一個新的構建版本而不是custom,最終的strings.xml將會是main目錄下的strings.xml。
manifest也和value檔案下的檔案一樣。如果你為你的構建版本建立了一個manifest檔案,那麼你不必要去拷貝在main檔案下的manifest檔案,你需要做的是新增標籤。Android外掛將會為你合併它們。
但是需要注意,當我們新增.java檔案到custom版本中,你可以新增相同的類到debug和release版本,但是不能新增到main版本。如果你新增了,會丟擲異常。這時候我們如果構建custom版本,那麼便會使用custom對應source set中的.java檔案。
依賴包
每一個構建版本都有自己的依賴包,gradle自動為每一個構建的版本建立不同的依賴配置。如果你想為debug版本新增一個logging框架,你可以這麼做:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
debugCompile 'de.mindpipe.android:android-logging-log4j:1.0.3'
}
product flavors
不同的生產版本。
product flavors極大簡化了基於相同的程式碼構建不同版本的app。
建立product flavors
android { productFlavors { vivo { applicationId "vivo" versionCode 1 minSdkVersion 15 } oppo { applicationId "oppo" versionCode 2 minSdkVersion 15 } } }
這時候AS 3.0以上會報錯ERROR: All flavors must now belong to a named flavor dimension
沒有給它們設定一個風味維度,我們可以加上
flavorDimensions "default"
flavorDimensions "default"
android {
productFlavors {
vivo {
applicationId "vivo"
versionCode 1
minSdkVersion 15
}
oppo {
applicationId "oppo"
versionCode 2
minSdkVersion 15
}
}
}
給這些產品版本預設一個風味維度,具體有何作用,等會會講。
這時候我們會在左下角的視窗看到這麼多個變體:
如果找不到該視窗,可以在這裡開啟:
由於我們之前在custom構建版本上設定了applicationIdSuffix ".custom",所以,當我們執行oppoCustom版本的時候,applicationId為oppo.custom
Source Set
product Flavors也有自己的程式碼資料夾。建立一個特殊的版本就像建立一個資料夾那麼簡單。如下圖所示:
但是值得注意的是,我們無法再vivo資料夾裡相同的包中新增構建版本已經有的.java檔案,新增了會報異常。而對於資原始檔,我們可以新增構建版本里面有的檔案,但是這個產品版本所對應的目錄的優先順序低於Build type,也就是說,當Custom目錄有一張圖片,vivo目錄也有一張圖片,那麼當我們執行打包vivoCustom版本的時候,使用的是custom裡面的圖片。除非我們建立的資料夾是vivoCustom,那麼它的優先順序便會是最高的。
Multiflavor variants
在某些情況下,你可能希望建立一些聯合的Product Flavors,這個時候便要使用到我們剛剛所說的flavorDimensions 了。
設想一下,假如我們需要打包兩個渠道的app:vivo和oppo,而這兩個渠道的app各自有付費版與免費版,那麼我們就需要用到多維度了。
首先定義兩個維度:渠道channel,付費與免費:money
flavorDimensions "channel","money"
flavorDimensions "channel","money"
android {
productFlavors {
vivo {
dimension "channel"
applicationId "vivo"
versionCode 1
minSdkVersion 15
}
oppo {
dimension "channel"
applicationId "oppo"
versionCode 2
minSdkVersion 15
}
free {
dimension "money"
}
vip {
dimension "money"
}
}
}
當你新增了flavor dimensions,你就需要為每個flavor新增dimension,否則會提示錯誤。
之後我們可以看到這麼多個變體:
而我們定義多個維度的順序是很重要的,因為當你在各個維度各自定義了同一個常量的值,比如:buildConfigField("String", "name","\"custom app\""),總是以第一維度的為準。
Build variants
構建變體
構建變體是構建版本和生產版本的結合體。當你建立了一個構建版本或者生產版本,同樣的,新的變體也會被建立。
像我們上圖便有這麼多的變體:
我們可以在這個視窗進行切換,然後執行不同的變體。
tasks
assembleDebug
一個是assembleRelease
來構建不同的APK。當新增一個新的Build Type的時候,一個新的Task也就會被建立,一旦你開始新增Flavors,一整套Tasks就會被建立,因為每一個BuildType的Tasks都會為每個Product Flavor聯合。Source sets
構建變體也可以有自己的資原始檔夾,舉個例子,你可以有src/vivoVipCustom/java/。原理與上面的類似
Resource and mainfest merging
Android Plugin需要在打包前對Main的SourceSet以及BuildType的SourceSet進行一次Merge。而且Library工程也會提供額外的資源,它們也會被Merge,例如Manifest.xml等等。也會在其中宣告一些許可權等。
Resource和Manifest.xml的優先順序順序如下:
如果一個資源在main中和在flavor中定義了,那麼那個在flavor中的資源有更高的優先順序。這樣那個在flavor資料夾中的資源將會被打包到apk。而在依賴專案申明的資源總是擁有最低優先順序。
當然,如果你建立的目錄是變體的目錄入:vivoVipCustom,那麼它的優先順序自然是高於Build type
建立構建變體
關於如何構建變數,上面已經說了,不再重複。
flavorDimensions "channel","money"
android {
productFlavors {
vivo {
dimension "channel"
applicationId "vivo"
versionCode 1
minSdkVersion 15
}
oppo {
dimension "channel"
applicationId "oppo"
versionCode 2
minSdkVersion 15
}
free {
dimension "money"
resValue "color", "colorfree", "#ff8888"
}
vip {
dimension "money"
resValue "color", "colorfree", "#ff0000"
}
}
}
resValue "color", "colorfree", "#ff8888"表示新增顏色名為colorfree的顏色
變體過濾器
忽略某個變體也是可行的。這樣你可以加速你的構建當使用assemble的時候,這樣你列出的tasks將不會執行那麼你不需要的變體。你可以使用過濾器,在build.gradle中新增程式碼如下所示:
android.variantFilter { variant ->
if(variant.buildType.name.equals('release')) {
variant.getFlavors().each() { flavor ->
if (flavor.name.equals('vivo')) { variant.setIgnore(true);
}
}
}
}
發現相關的變體不見了:
Signing Configurations
在你釋出你的應用之前,你需要為你的app私鑰簽名。如果你有付費版和免費版,你需要有不同的key去簽名不同的變體。這就是配置簽名的好處。配置簽名可以這樣定義:
android {
signingConfigs {
custom.initWith(signingConfigs.debug)
release {
storeFile file("release.keystore")
storePassword"secretpassword"
keyAlias "gradleforandroid"
keyPassword "secretpassword"
}
}
}
在這個例子中,我們建立了2個不同的簽名配置。debug配置是as預設的,其使用了公共的keystore和password,所以沒有必要為debug版本建立簽名配置了。custom配置使用了initWith()方法,其會複製其他的簽名配置。這意味著custom和debug的key是一樣的。
release配置使用了storeFile,定義了key alias和密碼。當然這不是一個好的選擇,你需要在 Gradle properties檔案中配置。
當你定義了簽名配置後,你需要應用它們。構建版本都有一個屬性叫做signingConfig,你可以這麼幹:
android {
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
上例使用了buildTypes,但是你可能需要對每個版本生成不同的驗證,你可以這麼定義:
android {
productFlavors {
vivo{
signingConfig signingConfigs.release
}
}
}
當簽名一個Flavor版本的時候,你需要重寫BuildType中的簽名配置。當需要使用相同的BuildType不同版本的Flavors的簽名時,可以通過下述方式:
android {
buildTypes {
release {
productFlavors.vivo.signingConfig signingConfigs.vivo
productFlavors.oppo.signingConfig signingConfigs.oppo
}
}
}
上面這個例子展示瞭如何在vivo
和oppo的Release版本使用不同的簽名,但是卻不影響Debug和Custom的BuildType。