安卓build variant ----Gradle for Android( 構建變體 )
當你在開發一個app,通常你會有幾個版本。大多數情況是你需要一個開發版本,用來測試app和弄清它的質量,然後還需要一個生產版本。這些版本通常有不同的設定,例如不同的URL地址。更可能的是你可能需要一個免費版和收費版本。基於上述情況,你需要處理不同的版本:開發免費版,開發付費版本,生產免費版,生產付費版,而針對不同的版本不同的配置,這極大增加的管理難度。
Gradle有一些方便的方法來管理這些問題。我們很早之前談過debug和release版本,現在我們談到另外一個概念,不同的產品版本。構建版本和生產版本通常可以合併,構建版本和生產版本的合併版叫做構建變種。
這一章我們將學習構建版本,它能使得開發更有效率,並且學習如何使用它們。然後我們會討論構建版本和生產版本的不同,以及如何將其合併。我們會探討簽名機制,如何針對不同的變種簽名等。
在這一章,我們遵循如下規則:
-
Build types
-
Product flavors
-
Build variants
-
Signing configurations
構建版本
在Gradle的Android外掛中,一個構建版本意味著定義一個app或者依賴庫如何被構建。每個構建版本都要特殊的一面,比如是否需要debug,application id是什麼,是否不需要的資源被刪除等等。你可以定義一個構建版本通過buildTypes方法。例如:
android {
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile
('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
這個檔案定義了該模組是release版本,然後定義了proguard的位置。該release版本不是唯一的構建版本,預設情況下,還有個debug版本。Android studio把它視為預設構建版本。
建立自己的構建版本
當預設的構建版本不夠用的時候,建立版本也是很容易的一件事,建立構建版本你只需要在buildTypes寫入自己的版本。如下所示:
android {
buildTypes {
staging {
applicationIdSuffix ".staging"
versionNameSuffix "-staging"
buildConfigField "String", "API_URL",
"\"http://staging.example.com/api\""
}
}
}
我們定義了一個staging版本,該版本定義了一個新的application id,這讓其與debug和release版本的applicationID不同。假設你使用了預設的配置,那麼applicationID將會是這樣的:
-
Debug: com.package
-
Release: com.package
-
Staging: com.package.staging
這意味著你可以在你的裝置上安裝staging版本和release版本。staging版本也有自己的版本號。buildConfigField定義了一個新的URL地址。你不必事事都去建立,所以最可能的方式是去繼承已有的版本。
android {
buildTypes {
staging.initWith(buildTypes.debug)
staging {
applicationIdSuffix ".staging"
versionNameSuffix "-staging"
debuggable = false
}
}
}
initWith()方法建立了一個新的版本的同時,複製所有存在的構建版本,類似繼承。我們也可以複寫該存在版本的所有屬性。
Source sets
當你建立了一個新的構建版本,Gradle也建立了新的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
├── staging
│ ├── java
│ │ └── com.package
├── drawable
└── layout
└── activity_main.xml
│ │
│ ├── res
│ │ └── layout
│ │ └── activity_main.xml
│ └── AndroidManifest.xml
└── release
├── java
│ └── com.package
│ └── Constants.java
└── AndroidManifest.xml
注意:當你新增一個Java類的時候,你需要知道以下過程,當你添加了CustomLogic.java到staging版本,你可以新增相同的類到debug和release版本,但是不能新增到main版本。如果你添加了,會丟擲異常。
當使用不同的source sets的時候,資原始檔的處理需要特殊的方式。Drawables和layout檔案將會複寫在main中的重名檔案,但是values檔案下的資源不會。gradle將會把這些資源連同main裡面的資源一起合併。
舉個例子,當你在main中建立了一個srings.xml的時候:
<resources>
<string name="app_name">TypesAndFlavors</string>
<string name="hello_world">Hello world!</string>
</resources>
當你在你的staing版本也添加了rings.xml:
<resources>
<string name="app_name">TypesAndFlavors STAGING</string>
</resources>
然後合併的strings.xml將會是這樣的:
<resources>
<string name="app_name">TypesAndFlavors STAGING</string>
<string name="hello_world">Hello world!</string>
</resources>
當你建立一個新的構建版本而不是staging,最終的strings.xml將會是main目錄下的strings.xml。
manifest也和value檔案下的檔案一樣。如果你為你的構建版本建立了一個manifest檔案,那麼你不必要去拷貝在main檔案下的manifest檔案,你需要做的是新增標籤。Android外掛將會為你合併它們。
我們將在會之後的章節講到合併的更多細節。
依賴包
每一個構建版本都有自己的依賴包,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建立不同版本。典型的例子是,一個app有付費和免費版。product flavors極大簡化了基於相同的程式碼構建不同版本的app。
如果你不確定你是否需要一個新的構建版本或者product flavors,你應該問你自己,你是否需要內部使用和外部使用的apk。如果你需要一個完全新的app去釋出,和之前的版本完全隔離開,那麼你需要product flavors。否則你只是需要構建版本。
建立product flavors
建立product flavors非常的容易。你可以在productFlavors中新增程式碼:
android {
productFlavors {
red {
applicationId 'com.gradleforandroid.red'
versionCode 3
}
blue {
applicationId 'com.gradleforandroid.blue'
minSdkVersion 14
versionCode 4
}
}
}
product flavors和構建版本的配置不同。因為product flavors有自己的ProductFlavor類,就像defaultConfig,這意味著你的所有productFlavors都分享一樣的屬性。
Source sets
就像構建版本一樣,product Flavors也有自己的程式碼資料夾。建立一個特殊的版本就像建立一個資料夾那麼簡單。舉個例子,當你有的生產版本的blue flavors有一個不同的app圖示,該資料夾需要被叫做blueRelease。
多個flavors構建變體
在一些例子中,你可能需要建立一些product flavors的合併版本。舉個例子,client A和client B可能都想要一個free和paid的版本,而他們又都是基於一樣的程式碼,但是有不一樣的顏色等。建立四個不同的flavors意味著有重複的配置。合併flavors最簡單的做法可能是使用flavor dimensions,就像這樣:
android {
flavorDimensions "color", "price"
productFlavors {
red {
flavorDimension "color"
}
blue {
flavorDimension "color"
}
free {
flavorDimension "price"
}
paid {
flavorDimension "price"
}
}
}
當你添加了flavor dimensions,你就需要為每個flavor新增flavorDimension,否則會提示錯誤。flavorDimensions定義了不同的dimensions,當然其順序也很重要。當你合併二個不同的flavors時,他們可能有一樣的配置和資源。例如上例:
-
blueFreeDebug and blueFreeRelease
-
bluePaidDebug and bluePaidRelease
-
redFreeDebug and redFreeRelease
-
redPaidDebug and redPaidRelease
構建變體
構建變體是構建版本和生產版本的結合體。當你建立了一個構建版本或者生產版本,同樣的,新的變體也會被建立。舉個例子,當你有debug和release版本,你建立了red和blue的生產版本,那麼變體將會有四個:
你可以在Android studio的左下角找到它,或者通過VIEW|Tool Windows|Build Variants開啟它。該檢視列出了所有的變體,並且允許你去切換它們。改變他們將會影響到你按Run按鈕。
如果你沒有product flavors,那麼變體只是簡單的包含構建版本,就算你沒有定義任何構建版本,Android studio也會預設為你建立debug版本的。
tasks
android外掛回味每一個變體建立不同的配置。一個新的Android專案會有debug和release版本,所有你可以使用assembleDebug和assembleRelease,當然當你使用assemble命令,會二者都執行。當你添加了一個新的構建版本,新的task也會被建立。例如:
-
assembleBlue uses the blue flavor configuration and assembles both BlueRelease and BlueDebug.
-
assembleBlueDebug combines the flavor configuration with the build type configuration, and the flavor settings override the build type settings.
Source sets
構建變體也可以有自己的資原始檔夾,舉個例子,你可以有src/blueFreeDebug/java/。
資原始檔和manifest的合併
在打包app之前,Android外掛會合並main中的程式碼和構建的程式碼。當然,依賴專案也可以提供額外的資源,它們也會被合併。你可能需要額外的Android許可權針對debug變體。舉個例子,你不想在main中申明這個許可權,因為這可能導致一些問題,所以你可以新增一個額外的mainfest檔案在debug的資料夾中,申明額外的許可權。
資源和mainfests的優先順序是這樣的:
如果一個資源在main中和在flavor中定義了,那麼那個在flavor中的資源有更高的優先順序。這樣那個在flavor資料夾中的資源將會被打包到apk。而在依賴專案申明的資源總是擁有最低優先順序。
建立構建變體
gradle讓處理構建變體變得容易。
android {
buildTypes {
debug {
buildConfigField "String", "API_URL",
"\"http://test.example.com/api\""
}
staging.initWith(android.buildTypes.debug)
staging {
buildConfigField "String", "API_URL",
"\"http://staging.example.com/api\""
applicationIdSuffix ".staging"
}
}
productFlavors {
red {
applicationId "com.gradleforandroid.red"
resValue "color", "flavor_color", "#ff0000"
}
blue {
applicationId "com.gradleforandroid.blue"
resValue "color", "flavor_color", "#0000ff"
}
}
}
在這個例子中,我們建立了4個變體,分別是blueDebug,blueStaging,redDebug,redStaging。每一個變體都有其不同的api url以及顏色。例如:
變體過濾器
忽略某個變體也是可行的。這樣你可以加速你的構建當使用assemble的時候,這樣你列出的tasks將不會執行那麼你不需要的變體。你可以使用過濾器,在build.gradle中新增程式碼如下所示:
android.variantFilter { variant ->
if(variant.buildType.name.equals('release')) {
variant.getFlavors().each() { flavor ->
if (flavor.name.equals('blue')) { variant.setIgnore(true);
}
}
}
}
在這個例子中,我們檢查下:
你可以看到blueFreeRelease和bluePaidRelease被排除在外,如果你執行gradlew tasks,你會發現所有的關於上述變體的tasks不再存在。
簽名配置
在你釋出你的應用之前,你需要為你的app私鑰簽名。如果你有付費版和免費版,你需要有不同的key去簽名不同的變體。這就是配置簽名的好處。配置簽名可以這樣定義:
android {
signingConfigs {
staging.initWith(signingConfigs.debug)
release {
storeFile file("release.keystore")
storePassword"secretpassword"
keyAlias "gradleforandroid"
keyPassword "secretpassword"
}
}
}
在這個例子中,我們建立了2個不同的簽名配置。debug配置是as預設的,其使用了公共的keystore和password,所以沒有必要為debug版本建立簽名配置了。staging配置使用了initWith()方法,其會複製其他的簽名配置。這意味著staging和debug的key是一樣的。
release配置使用了storeFile,定義了key alias和密碼。當然這不是一個好的選擇,你需要在 Gradle properties檔案中配置。
當你定義了簽名配置後,你需要應用它們。構建版本都有一個屬性叫做signingConfig,你可以這麼幹:
android {
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
上例使用了buildTypes,但是你可能需要對每個版本生成不同的驗證,你可以這麼定義:
android {
productFlavors {
blue {
signingConfig signingConfigs.release
}
}
}
當然,你在flavor中定義這些,最好會被重寫,所以最好的做法是:
android {
buildTypes {
release {
productFlavors.red.signingConfig signingConfigs.red
productFlavors.blue.signingConfig signingConfigs.blue
}
}
}