目錄
一、前言
二、flavorDimensions 的意義
三、productFlavors的意義
四、productFlavor
五、寫在最後
一、前言
有了前兩篇博客的鋪墊,我們可以來分享下另外兩個參數了 flavorDimensions
和 productFlavors
,而這兩個參數成對出現,可以做一些差分化定義。
前兩篇博客爲:
1、defaultConfig——安卓gradle
2、buildTypes——安卓gradle
需要事先說明的是,接下來所說的 “意義” 並非官方文檔翻譯,而是結合了小盆友自己的理解,會比較口語化。
二、flavorDimensions 的意義
flavorDimensions 從單詞字面理解知道是 “風味維度”,是需要結合 “產品風味(即productFlavors)” 來一起使用的。
flavorDimensions 的使用會定義出維度,供接下來的 productFlavors 使用。我們舉個例子
android {
// 省略其他參數
flavorDimensions('abi', 'version')
}
使用上面代碼,則會定義出兩個維度:version 和 abi。一個參數一個維度,我們把它形象化,就可以看成下面這樣一張圖。而他有什麼作用,我們看下一小節。
如果三個參數就可以看成一個三維的空間座標系,這裏的座標系只是一個形象化的體現。
三、productFlavors的意義
productFlavors 從字面瞭解是“產品風味”。他需要和一個風味維度對接,否則會報錯。
接着我們上面的例子,使用 productFlavors 定義維度上的風味,使用 dimension
關聯。
android{
// 其他參數
flavorDimensions('abi', 'version')
// 創建產品風味
productFlavors {
v1 {
// 關聯緯度
dimension 'version'
}
v2 {
dimension 'version'
}
v3 {
dimension 'version'
}
x86 {
dimension 'abi'
}
armV7 {
dimension 'abi'
}
}
}
通過上面這段代碼,會形成下面這張圖。在 abi 維度上關聯了兩個產品,即 “armV7” 和 “x86”,在 version 的維度上關聯了三個產品,即 “v1”、“v2” 和 “v3”。
而這些維度的交織就會形成最終的風味,即我們上面所標出來的 “armV7V1”、“armV7V2”、“armV7V3”、“x86V1”、“x86V2”、“x86V3”。
我們可以根據不同的風味,打出不同的apk包,便可以實現一套核心代碼打出多個有些差異的包。
更多的使用我們在下篇博客講解,我們先了解下他可配置的參數。
四、productFlavor
productFlavors
下的每個項最終形式是 productFlavor
,即我們上面說所的 “v1”,“v2”…
1、productFlavor 存在形式
我們知道,每個配置最終會被映射爲一個類,或是一個屬性、或一個方法。productFlavor 也不例外,他會被映射爲 com.android.build.gradle.internal.dsl.ProductFlavor
,繼承結構如下
2、productFlavor 的屬性意義
我們先來一個約定,避免使用的代碼過於冗長。
android {
productFlavors{
zincPower{
// 我們下面的 “使用方法” 代碼都是基於這一塊,除非特殊說明。
}
}
}
2.1 applicationId
- 類型:String
- 描述:應用的id,這裏會覆蓋掉
defaultConfig
中配置的applicationId
。從而可以讓我們打出不同的apk包。
defaultConfig 的具體配置可以看小盆友的另一篇博文,傳送門。
- 使用方法:
zincPower {
// applicationId 應用的包名,會覆蓋 defaultConfig 中的 applicationId
// applicationId 會替換 AndroidManifest.xml 中的 manifest 標籤下 package 的 value
applicationId "com.zinc.power"
......省略其他配置
}
2.2 applicationIdSuffix
- 類型:String
- 描述:會追加在 applicationId 字符串的後面,形成最終的包名。這樣也可以達到一套代碼多個包名的效果。
- 使用方法:
zincPower {
applicationIdSuffix '.debug'
}
2.3 consumerProguardFiles
- 類型:List< File >
- 描述:這個屬性只作用於我們創建的
library
中,包括我們以aar形式導入的 library ,或是直接創建的 library。它的作用是,負責該 library 被進行編譯時的混淆規則,我們在 主App 的模塊下則可以不用再管理各個 library 的混淆規則,會直接使用各個 library 的混淆規則文件。 - 使用方法:
zincPower {
consumerProguardFiles 'consumer-rules.pro'
......省略其他配置
}
// 因爲該屬性是一個 List<File> 類型,如果需要多個文件配置,則如下所示
zincPower {
consumerProguardFiles 'consumer-rules.pro','zincPower-rules.pro'
......省略其他配置
}
2.4 dimension
- 類型:String
- 描述:風味的維度,指定我們當前風味所所屬的維度。一個風味必須要有一個維度,而且也只能關聯一個維度。 否則會報以下錯誤
ERROR: Flavor 'v1' has no flavor dimension.
- 使用方法:
zincPower {
// 關聯緯度
dimension 'version'
......省略其他配置
}
2.5 externalNativeBuild
- 類型:ExternalNativeBuildOptions
- 描述:這裏我們設置 ndk 編譯過程的一些參數。分爲 cmake 和 ndkBuild 兩個參數。
- 使用方法:
zincPower {
externalNativeBuild {
ndkBuild {
// Passes an optional argument to ndk-build.
arguments "NDK_MODULE_PATH+=../../third_party/modules"
}
// For ndk-build, instead use the ndkBuild block.
cmake {
// Passes optional arguments to CMake.
arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"
// Sets a flag to enable format macro constants for the C compiler.
cFlags "-D__STDC_FORMAT_MACROS"
// Sets optional flags for the C++ compiler.
cppFlags "-fexceptions", "-frtti"
// Specifies the library and executable targets from your CMake project
// that Gradle should build.
targets "libexample-one", "my-executible-demo"
}
}
}
2.6 javaCompileOptions
- 類型:JavaCompileOptions
- 描述:配置編譯時 java 的一些參數,例如我們使用
annotationProcessor
時所需要的參數。 - 使用方法:
zincPower {
javaCompileOptions {
annotationProcessorOptions{
arguments = []
classNames ''
....
}
}
......省略其他配置
}
JavaCompileOptions 可以配置的具體參數,請進傳送門
2.7 manifestPlaceholders
- 類型:Map<String, Object>
- 描述:配置可以在
AndroidManifest.xml
中替換的參數,我們可以使用這個參數配置不同風味的 logo 和 app名字,以及友盟的參數,達到不同風味的差異化配置。 - 使用方法:
我們配置差異化的 logo 和 app名字,則可以在 gradle 中使用下面這段
android {
flavorDimensions('abi', 'version')
productFlavors {
// 省略其他的風味配置
x86 {
dimension 'abi'
// 配置不同的包名,達到能兩個風味能共存
applicationId 'com.zinc.bear'
manifestPlaceholders = [
logo : "@drawable/logo_bear",
appName : "bear",
]
// 配置簽名
signingConfig signingConfigs.jiangpengyong
}
armV7 {
dimension 'abi'
// 配置不同的包名,達到能兩個風味能共存
applicationId 'com.zinc.shark'
manifestPlaceholders = [
logo : "@drawable/logo_shark",
appName : "shark",
]
// 配置簽名
signingConfig signingConfigs.xiaopenyou
}
}
}
logo的資源圖
然後在 AndroidManifest.xml
中使用,使用 ${你配置的變量名}
<application
android:allowBackup="true"
android:icon="${logo}"
android:label="${appName}"
android:roundIcon="${logo}"
android:supportsRtl="true"
android:theme="@style/AppTheme">
......
最終分別運行後,我們可以看到如下效果
2.8 matchingFallbacks
- 類型:List< String >
- 描述:用於處理引入的 library 中存在不匹配的風味情況。
- 使用方法:
舉個例子:
我們有一個 x86 的風味 在引入一個 library,而 library 中存在同樣的風味維度,但是沒有相同產品風味,這樣會導致沒法匹配,此時,就需要用這個參數。代碼如下:
app 下的 build.gradle
android {
// 其他配置
flavorDimensions('abi')
// 創建產品風味
productFlavors {
x86 {
dimension 'abi'
matchingFallbacks = ['pro']
}
}
dependencies {
// 引入 flavor_x86 library
x86Implementation project(':flavor_x86')
}
flavor_x86 下的 build.gradle
android {
// 存在相同的風味維度
flavorDimensions('abi')
// 沒有相同的產品風味,需要使用 matchingFallbacks 選擇需要的產品風味
productFlavors {
pro {
dimension 'abi'
}
free{
dimension 'abi'
}
}
}
小盆友在github上有個demo,有所幫助就給star吧。
2.9 multiDexEnabled
64K 引用限制問題官方文檔 傳送門
- 類型:Boolean
- 描述:是否開啓分包。因爲安卓中方法索引值爲兩個字節,四位十六進制的一個數值,即[0, 0xffff],所以最大方法數爲65536個。一旦超出了,就需要進行分包,所以我們就需要開啓這個參數。
- 值得一提:我們可以在 defaultConfig 中開啓即可,就不需要每個風味都寫一遍,減少冗餘。
爲了避免篇幅過長,使用方法請看小盆友的另一片博文:defaultConfig——安卓gradle 的 3.8小節
2.10 multiDexKeepFile
- 類型:File
- 描述:將我們需要的類打包進主包,即
classs.dex
。我們在第 2.9 小點,分享到使用了多 dex包處理,有時我們需要將一些主要的類打包進主包,則可以使用該屬性。 - 使用方法:
zincPower {
multiDexKeepFile file('multidex-config.txt')
...
}
multidex-config.txt 中的書寫則如下,每一個文件則爲一行
com/example/MyClass.class
com/example/TestClass.class
2.11 multiDexKeepProguard
- 類型:File
- 描述:將我們需要的類打包進主包,和第 2.10 點的功能相同,區別在於寫法。
- 使用方法:
zincPower {
multiDexKeepFile file('multidex-config.pro')
...
}
multidex-config.pro 中的寫法如下
// 將會保留所有的在com.example package的類
-keep class com.example.** { *; }
2.12 ndk
- 類型:NdkOptions
- 描述:用於abi過濾
- 使用方法:
進行如下配置,編譯出來的 Apk包只包含armeabi-v7a
,不會包含其他的架構,例如 “X86”。
zincPower {
// ndk中,目前只有 abiFilter 一個屬性,所以 ndk 目前來說只用於 abi 的過濾
ndk {
abiFilter 'armeabi-v7a'
}
...
}
NdkOptions的具體可配參數見官方文檔 傳送門
2.13 proguardFiles
- 類型:List< File >
- 描述:配置混淆規則文件,只有
minifyEnabled
設置爲 true 的時候會使用這個參數,文件中需要申明哪些文件不被優化和混淆。 - 值得一提:因爲被混淆後端代碼,類名和方法名都會有所變化,所以進行反射會失敗,這是我們就需要進行申明他們不被混淆。(這裏只是舉了使用這個參數的一個場景,如果應用本來是正常的,開了混淆後,出現了莫名奇妙的bug,那就思考下是否因爲混淆導致了這一bug)
- 使用方法:
zincPower {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
2.14 signingConfig
- 類型:SigningConfig
SigningConfig 的可配置參數 傳送門
- 描述:配置簽名配置。apk包能被安裝是需要被簽名的,我們直接運行的時候,是使用了系統默認的簽名證書,當我們要發佈release包時,則需要使用屬於個人或企業的簽名。
- 使用方法:
(1)我們需要先在項目根目錄下創建一個 keystore.properties 文件,在文件中寫入對應的配置,如圖所示
(2)在應用級的 build.gradle 中使用如下代碼
// 引入我們在(1)中創建的配置
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
compileSdkVersion 28
buildToolsVersion "29.0.1"
defaultConfig {
// 省略一些配置...
}
signingConfigs {
xiaopenyou {
// 使用 keystoreProperties 獲取對應的參數
keyAlias keystoreProperties['keyAlias2']
keyPassword keystoreProperties['keyPassword2']
storeFile file(keystoreProperties['storeFile2'])
storePassword keystoreProperties['storePassword2']
}
// ...其他簽名配置
}
buildTypes {
// 省略一些配置...
}
flavorDimensions('abi', 'version')
// 創建產品風味
productFlavors {
// 省略一些配置...
armV7 {
// 省略一些配置...
signingConfig signingConfigs.xiaopenyou
}
}
}
2.15 vectorDrawables
- 類型:VectorDrawablesOptions
- 描述:配置矢量圖的參數
- 使用方法:
VectorDrawablesOptions 中只有兩個參數,爲generatedDensities
和useSupportLibrary
。分別的用處如下
zincPower {
vectorDrawables {
// 如果 minSdkVersion 小於 21,只生成mdpi的png
generatedDensities 'mdpi'
// 設置爲 true,會忽略 generatedDensities ,會加入svg兼容包,不會再產生png
useSupportLibrary true
}
}
矢量圖的用法,可以看小盆友的另一片文章
2.16 versionCode
- 類型:Integer
- 描述:應用當前的版本值。和
versionName
的區別在小盆友看來,versionCode
是給程序員看的,versionName
是給產品經理和用戶看的。 - 使用方法:
zincPower {
versionCode 1000
......
}
2.17 versionName
- 類型:String
- 描述:應用版本。我們通常所說的該應用的版本是“1.2.0”,則是由這個值配置的。
- 使用方法:
zincPower {
versionName "1.0.0"
.....
}
2.18 versionNameSuffix
- 類型:String
- 描述:追加在第 2.17 小點“版本”的後綴
- 使用方法:
zincPower {
// 如果 versionName "1.0.0" ,則最終的版本名爲 1.0.0.test
versionNameSuffix ".test"
.....
}
3、productFlavor 的方法意義
3.1 buildConfigField(type, name, value)
- 描述:我們可以在 BuildConfig 類中添加值,最終會在 BuildConfig 中添加如下一行代碼。
// 值的注意的是 value 的值是原樣放置,我們通過使用方法一節來了解
<type> <name> = <value>
- 使用方法:
productFlavors {
x86 {
// 可以通過 BuildConfig 進行獲取
buildConfigField('String', 'name', '"XiaoPenYou"')
buildConfigField('int', 'age', '26')
.....
}
}
最終會生成如下圖的配置,我們可以通過下面代碼進行獲取
String name = BuildConfig.name;
int age = BuildConfig.age;
值的一提的是,我們設置 String 類型的參數時,需要加上 “” 雙引號(如例子中的name屬性)。切記!
3.2 consumerProguardFile(proguardFile)
- 描述:和上面分享的 2.3 小點的屬性 consumerProguardFiles 是一樣的作用。只是這裏只能設置一個 混淆文件。
- 使用方法:
zincPower {
consumerProguardFile('consumer-rules.pro')
}
3.3 consumerProguardFiles(proguardFiles)
- 描述:和上面分享的 2.3 小點的屬性 consumerProguardFiles 是一樣的作用,而且也是多個混淆文件。
- 使用方法:
zincPower {
consumerProguardFile('consumer-rules.pro', 'zincPower-rules.pro',.....)
}
3.4 maxSdkVersion(maxSdkVersion)
- 描述:設置應用的最高支持版本,一般我們不會進行設置
- 使用方法:
zincPower {
// 最高支持28版本
minSdkVersion 28
}
3.5 minSdkVersion(minSdkVersion)
- 描述:設置應用的最低支持版本
- 使用方法:
zincPower {
// 最低支持19版本
minSdkVersion 19
}
3.6 missingDimensionStrategy(dimension, requestedValue)
- 相似方法:missingDimensionStrategy(dimension, requestedValues) 區別在於第二個參數可以設置多個風味。
- 參數說明:
(1)dimension:維度
(2)requestedValue:風味(如果爲 requestedValues 則是風味列表) - 描述:忽略在 Library 中的渠道設置,即維度(dimension)和風味(flavor),如果不進行忽略,在進行引入的時候會無法進行。
- 使用方法:
我們的項目結構如下
在 zinclibrary
的 build.gradle
中編寫了如下渠道配置
// 創建 風味維度
flavorDimensions('zinc', 'handsome')
// 創建產品風味
productFlavors {
minApi13{
dimension 'zinc'
}
minApi23{
dimension 'zinc'
}
x86{
dimension 'handsome'
}
arm64{
dimension 'handsome'
}
}
此時如果直接在 app
的 build.gradle
中添加依賴,同步時便會出錯
dependencies {
...忽略其他依賴
implementation project(":zinclibrary")
}
所以我們需要在 app
的 build.gradle
中使用這個參數進行忽略 library 中帶來的維度和風味,即使用如下代碼
zincPower {
missingDimensionStrategy 'zinc', 'minApi13', 'minApi23'
missingDimensionStrategy 'handsome', 'x86', 'arm64'
}
3.7 proguardFile(proguardFile)
- 描述:添加混淆文件,和 2.13小點 的功能一致,只是傳入的是一個文件,這裏就不再贅述
- 使用方法:
zincPower {
proguardFile 'proguard-rules.pro'
}
3.10 proguardFiles(files)
- 描述:添加混淆文件,和 2.13小點 的功能一致,這裏就不再贅述
- 使用方法:
debug {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
3.11 resConfig(config)
- 描述:保留的資源配置。
- 使用用法:
zincPower {
// 這樣我們編譯出的apk中,只有 “默認” 和 “中文zh” 兩種資源
resConfig "zh"
}
3.12 resConfigs(config)
- 描述:保留的資源配置,和 resConfig 的區別在於,resConfigs 保留多個資源。
- 使用用法:
zincPower {
// 這樣我們編譯出的apk中,只有 “默認” 、 “中文zh” 和 “英文en” 兩種資源
resConfigs "zh","en"
}
3.13 resValue(type, name, value)
- 描述:添加 value 資源
- 使用用法:
zincPower {
// 添加至 res/value,通過 R.string.age 獲取
resValue('string', 'age', '12year')
}
3.14 setConsumerProguardFiles(proguardFileIterable)
- 描述:和 2.3小點 的功能一致,只是寫法不同,這裏就不再贅述
- 使用方法:
zincPower {
consumerProguardFiles = [ 'consumer-rules.pro','zincPower-rules.pro' ]
}
3.15 setProguardFiles(proguardFileIterable)
- 描述:添加混淆文件,和 2.13小點 的功能一致,只是寫法稍微不同,這裏就不再贅述
- 使用方法:
zincPower {
proguardFiles = [getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro']
}
3.16 targetSdkVersion(targetSdkVersion)
- 描述:應用的目標版本。說明我們已經對指定的版本進行了測試,在開發過程中可以使用至該版本的API,否則會被提示無法使用。如果不設置,則和 minSdkVersion 的值保持一致。
- 使用用法:
zincPower {
targetSdkVersion 28
}
五、寫在最後
Gradle 的配置文件看起來好像挺亂,其實是因爲我們沒有進行整體的梳理,所謂 “無知最可怕”。
如果喜歡的話請給我一個贊,並關注我吧。文章中如有寫的不妥的地方,請評論區或加我微信與我討論吧,共同進步。
歡迎加我微信,進行更多的交流