當你在開發一個app,通常你會有幾個版本。大多數情況是你需要一個開發版本,用來測試app和弄清它的質量,然后還需要一個生產版本。這些版本通常有不同的設置,例如不同的URL地址。更可能的是你可能需要一個免費版和收費版本?;谏鲜銮闆r,你需要處理不同的版本:開發免費版,開發付費版本,生產免費版,生產付費版,而針對不同的版本不同的配置,這極大增加的管理難度。
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()方法創建了一個新的版本的同時,復制所有存在的構建版本,類似繼承。我們也可以復寫該存在版本的所有屬性。
So
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
創建構建變體
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以及顏色。例如:
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。而在依賴項目申明的資源總是擁有最低優先級。
忽略某個變體也是可行的。這樣你可以加速你的構建當使用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);
}
}
}
}
在這個例子中,我們檢查下:
變體過濾器
忽略某個變體也是可行的。這樣你可以加速你的構建當使用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
}
}
}
總結
在這一章,我們討論了構建版本和生產版本,以及如何結合它們。這將會是非常有用的工具,當你需要不同的url以及不同的keys,而你們有相同的代碼和資源文件,但是有不同的類型以及版本,構建版本和生產版本將會讓你的生活更美好。
我們也談論了簽名配置以及如何使用他們。
下一章,你將會學習到多模塊構建,因為當你想把你的代碼分成一個依賴包或者依賴項目的時候,或者你想把Android wear模塊放在你的應用的時候,這將非常重要。
原文:https://segmentfault.com/a/1190000004241503
關注微信公眾號獲得更多內容: