Gradle中Flavor使用

一、前言

本文主要分享Gradle中兩個參數(shù)flavorDimensions和productFlavors,而這兩個參數(shù)成對出現(xiàn),可以做一些差分化定義。需要事先說明的是,接下來所說的 “意義” 并非官方文檔翻譯,而是結(jié)合了自己的理解,會比較口語化。


二、flavorDimensions 的意義

flavorDimensions從單詞字面理解知道是 “風味維度”,是需要結(jié)合 “產(chǎn)品風味(即productFlavors)” 來一起使用的。flavorDimensions的使用會定義出維度,供接下來的 productFlavors 使用。我們舉個例子

android {
    // 省略其他參數(shù)
    flavorDimensions('abi', 'version')
}

使用上面代碼,則會定義出兩個維度:version和abi。一個參數(shù)一個維度,我們把它形象化,就可以看成下面這樣一張圖。而他有什么作用,我們看下一小節(jié)。

安卓_AS中Flavor使用_內(nèi)容1.png

如果三個參數(shù)就可以看成一個三維的空間坐標系,這里的坐標系只是一個形象化的體現(xiàn)。


三、productFlavors的意義

productFlavors 從字面了解是“產(chǎn)品風味”。他需要和一個風味維度對接,否則會報錯。接著我們上面的例子,使用 productFlavors 定義維度上的風味,使用 dimension 關(guān)聯(lián)。

android{
    // 其他參數(shù)
    flavorDimensions('abi', 'version')
    // 創(chuàng)建產(chǎn)品風味
    productFlavors {
        v1 {
            // 關(guān)聯(lián)緯度
            dimension 'version'
        }
        v2 {
            dimension 'version'
        }
        v3 {
            dimension 'version'
        }
        x86 {
            dimension 'abi'
        }
        armV7 {
            dimension 'abi'
        }
    }
}

通過上面這段代碼,會形成下面這張圖。在 abi 維度上關(guān)聯(lián)了兩個產(chǎn)品,即 “armV7” 和 “x86”,在 version 的維度上關(guān)聯(lián)了三個產(chǎn)品,即 “v1”、“v2” 和 “v3”。

安卓_AS中Flavor使用_內(nèi)容2.png

而這些維度的交織就會形成最終的風味,即我們上面所標出來的 “armV7V1”、“armV7V2”、“armV7V3”、“x86V1”、“x86V2”、“x86V3”。我們可以根據(jù)不同的風味,打出不同的apk包,便可以實現(xiàn)一套核心代碼打出多個有些差異的包。我們先了解下它可配置的參數(shù)。


四、productFlavor

productFlavors 下的每個項最終形式是 productFlavor,即我們上面說所的 “v1”,“v2”…

1. productFlavor 存在形式

我們知道,每個配置最終會被映射為一個類,或是一個屬性、或一個方法。productFlavor 也不例外,他會被映射為com.android.build.gradle.internal.dsl.ProductFlavor,繼承結(jié)構(gòu)如下

graph LR
    dPF[dsl/ProductFlavor] --> BF[BaseFlavor]
    BF --> DPF[DefaultProductFlavor]
    DPF --> BCI[BaseConfigImpl]
    DPF -.-> PF[ProductFlavor]
    BCI -.-> BC[BaseConfig]
    PF --> BC
    PF --> DA[DimensionAware]
    BF -.-> CPF[CoreProductFlavor]
    CPF --> N[Named]
    CPF --> PF

2. productFlavor 的屬性意義

我們先來一個約定,避免使用的代碼過于冗長。

android {
    productFlavors{
        zincPower{
        // 我們下面的 “使用方法” 代碼都是基于這一塊,除非特殊說明。
        }
    }
}

2.1 applicationId

  • 類型:String
  • 描述:應用的id,這里會覆蓋掉 defaultConfig 中配置的 applicationId。從而可以讓我們打出不同的apk包。
  • 使用方法:
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 >
  • 描述:這個屬性只作用于我們創(chuàng)建的 library 中,包括我們以aar形式導入的 library ,或是直接創(chuàng)建的 library。它的作用是,負責該 library 被進行編譯時的混淆規(guī)則,我們在 主App 的模塊下則可以不用再管理各個 library 的混淆規(guī)則,會直接使用各個 library 的混淆規(guī)則文件。
  • 使用方法:
zincPower {
    consumerProguardFiles 'consumer-rules.pro'
    ......省略其他配置
}
// 因為該屬性是一個 List<File> 類型,如果需要多個文件配置,則如下所示
zincPower {
    consumerProguardFiles 'consumer-rules.pro','zincPower-rules.pro'
    ......省略其他配置
}

2.4 dimension

  • 類型:String
  • 描述:風味的維度,指定我們當前風味所所屬的維度。一個風味必須要有一個維度,而且也只能關(guān)聯(lián)一個維度。 否則會報以下錯誤
ERROR: Flavor 'v1' has no flavor dimension.
  • 使用方法:
zincPower {
    // 關(guān)聯(lián)緯度
    dimension 'version'
    ......省略其他配置
}

2.5 externalNativeBuild

  • 類型:ExternalNativeBuildOptions
  • 描述:這里我們設置 ndk 編譯過程的一些參數(shù)。分為 cmake 和 ndkBuild 兩個參數(shù)。
  • 使用方法:
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 的一些參數(shù),例如我們使用 annotationProcessor 時所需要的參數(shù)。
  • 使用方法:
zincPower {
    javaCompileOptions {
        annotationProcessorOptions{
            arguments = []
            classNames ''
            ....
        }
    }
    ......省略其他配置
}

2.7 manifestPlaceholders

  • 類型:Map<String, Object>
  • 描述:配置可以在 AndroidManifest.xml 中替換的參數(shù),我們可以使用這個參數(shù)配置不同風味的 logo 和 app名字,以及友盟的參數(shù),達到不同風味的差異化配置。
  • 使用方法:

我們配置差異化的 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資源圖:

安卓_AS中Flavor使用_內(nèi)容3.png
安卓_AS中Flavor使用_內(nèi)容3.png
安卓_AS中Flavor使用_內(nèi)容4.png

然后在 AndroidManifest.xml 中使用,使用 ${你配置的變量名}

<application
        android:allowBackup="true"
        android:icon="${logo}"
        android:label="${appName}"
        android:roundIcon="${logo}"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
......

最終分別運行后,我們可以看到如下效果

安卓_AS中Flavor使用_內(nèi)容5.png

2.8 matchingFallbacks

  • 類型:List< String >
  • 描述:用于處理引入的 library 中存在不匹配的風味情況。
  • 使用方法:

舉個例子:
我們有一個 x86 的風味在引入一個 library,而 library 中存在同樣的風味維度,但是沒有相同產(chǎn)品風味,這樣會導致沒法匹配,此時,就需要用這個參數(shù)。代碼如下:

app 下的 build.gradle

android {
    // 其他配置
    flavorDimensions('abi')
    // 創(chuàng)建產(chǎn)品風味
    productFlavors {
        x86 {
            dimension 'abi'
            matchingFallbacks = ['pro']
        }
}

dependencies {
    // 引入 flavor_x86 library
    x86Implementation project(':flavor_x86')
}

flavor_x86 下的 build.gradle

android {
    // 存在相同的風味維度
    flavorDimensions('abi')
    // 沒有相同的產(chǎn)品風味,需要使用 matchingFallbacks 選擇需要的產(chǎn)品風味
    productFlavors {
        pro {
            dimension 'abi'
        }
        free{
            dimension 'abi'
        }
    }
}

2.9 multiDexEnabled

  • 類型:Boolean
  • 描述:是否開啟分包。因為安卓中方法索引值為兩個字節(jié),四位十六進制的一個數(shù)值,即[0, 0xffff],所以最大方法數(shù)為65536個。一旦超出了,就需要進行分包,所以我們就需要開啟這個參數(shù)。
  • 值得一提:我們可以在 defaultConfig 中開啟即可,就不需要每個風味都寫一遍,減少冗余。

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 點的功能相同,區(qū)別在于寫法。
  • 使用方法:
zincPower  {
    multiDexKeepFile file('multidex-config.pro')
    ...
}

multidex-config.pro 中的寫法如下

// 將會保留所有的在com.example package的類
-keep class com.example.** { *; }

2.12 ndk

  • 類型:NdkOptions
  • 描述:用于abi過濾
  • 使用方法:
    進行如下配置,編譯出來的 Apk包只包含armeabi-v7a,不會包含其他的架構(gòu),例如 “X86”。
zincPower {
    // ndk中,目前只有 abiFilter 一個屬性,所以 ndk 目前來說只用于 abi 的過濾
    ndk {
        abiFilter 'armeabi-v7a'
    }
    ...
}

2.13 proguardFiles

  • 類型:List< File >
  • 描述:配置混淆規(guī)則文件,只有 minifyEnabled 設置為 true 的時候會使用這個參數(shù),文件中需要申明哪些文件不被優(yōu)化和混淆。
  • 值得一提:因為被混淆后端代碼,類名和方法名都會有所變化,所以進行反射會失敗,這是我們就需要進行申明他們不被混淆。(這里只是舉了使用這個參數(shù)的一個場景,如果應用本來是正常的,開了混淆后,出現(xiàn)了莫名奇妙的bug,那就思考下是否因為混淆導致了這一bug
  • 使用方法:
zincPower {
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}

2.14 signingConfig

  • 類型:SigningConfig
  • 描述:配置簽名配置。apk包能被安裝是需要被簽名的,我們直接運行的時候,是使用了系統(tǒng)默認的簽名證書,當我們要發(fā)布release包時,則需要使用屬于個人或企業(yè)的簽名。
  • 使用方法:
  1. 我們需要先在項目根目錄下創(chuàng)建一個 keystore.properties 文件,在文件中寫入對應的配置,如圖所示
安卓_AS中Flavor使用_內(nèi)容6.png
  1. 在應用級的 build.gradle 中使用如下代碼
// 引入我們在(1)中創(chuàng)建的配置
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 獲取對應的參數(shù)
            keyAlias keystoreProperties['keyAlias2']
            keyPassword keystoreProperties['keyPassword2']
            storeFile file(keystoreProperties['storeFile2'])
            storePassword keystoreProperties['storePassword2']
        }
        // ...其他簽名配置
    }

    buildTypes {
        // 省略一些配置...
    }

    flavorDimensions('abi', 'version')
    // 創(chuàng)建產(chǎn)品風味
    productFlavors {
        // 省略一些配置...
        armV7 {
            // 省略一些配置...
            signingConfig signingConfigs.xiaopenyou
        }
    }
}

2.15 vectorDrawables

  • 類型:VectorDrawablesOptions
  • 描述:配置矢量圖的參數(shù)
  • 使用方法:
    VectorDrawablesOptions 中只有兩個參數(shù),為 generatedDensitiesuseSupportLibrary。分別的用處如下
zincPower {
    vectorDrawables {
        // 如果 minSdkVersion 小于 21,只生成mdpi的png
        generatedDensities 'mdpi'
        // 設置為 true,會忽略 generatedDensities ,會加入svg兼容包,不會再產(chǎn)生png
        useSupportLibrary true
    }
}

2.16 versionCode

  • 類型:Integer
  • 描述:應用當前的版本值。和 versionName 的區(qū)別在小盆友看來,versionCode 是給程序員看的,versionName 是給產(chǎn)品經(jīng)理和用戶看的。
  • 使用方法:
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 的值是原樣放置,我們通過使用方法一節(jié)來了解
<type> <name> = <value>
  • 使用方法:
productFlavors {
    x86 {
        // 可以通過 BuildConfig 進行獲取
        buildConfigField('String', 'name', '"XiaoPenYou"')
        buildConfigField('int', 'age', '26')
        .....
    }
}

最終會生成如下圖的配置,我們可以通過下面代碼進行獲取

String name = BuildConfig.name;
int age = BuildConfig.age;
安卓_AS中Flavor使用_內(nèi)容7.png

值的一提的是,我們設置 String 類型的參數(shù)時,需要加上 “” 雙引號(如例子中的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) 區(qū)別在于第二個參數(shù)可以設置多個風味。
  • 參數(shù)說明:
    • dimension:維度
    • requestedValue:風味(如果為 requestedValues 則是風味列表)
  • 描述:忽略在 Library 中的渠道設置,即維度(dimension)和風味(flavor),如果不進行忽略,在進行引入的時候會無法進行。
  • 使用方法:

我們的項目結(jié)構(gòu)如下

安卓_AS中Flavor使用_內(nèi)容8.png

zinclibrarybuild.gradle 中編寫了如下渠道配置

// 創(chuàng)建 風味維度
flavorDimensions('zinc', 'handsome')
// 創(chuàng)建產(chǎn)品風味
productFlavors {
    minApi13{
        dimension 'zinc'
    }
    minApi23{
        dimension 'zinc'
    }
    x86{
        dimension 'handsome'
    }
    arm64{
        dimension 'handsome'
    }
}

此時如果直接在 appbuild.gradle 中添加依賴,同步時便會出錯

dependencies {
    ...忽略其他依賴
    implementation project(":zinclibrary")
}

所以我們需要在 appbuild.gradle 中使用這個參數(shù)進行忽略 library 中帶來的維度和風味,即使用如下代碼

zincPower {
    missingDimensionStrategy 'zinc', 'minApi13', 'minApi23'
    missingDimensionStrategy 'handsome', 'x86', 'arm64'
}

3.7 proguardFile(proguardFile)

  • 描述:添加混淆文件,和 2.13小點 的功能一致,只是傳入的是一個文件,這里就不再贅述
  • 使用方法:
zincPower {
    proguardFile 'proguard-rules.pro'
}

3.8 proguardFiles(files)

  • 描述:添加混淆文件,和 2.13小點 的功能一致,這里就不再贅述
  • 使用方法:
debug {
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}

3.9 resConfig(config)

  • 描述:保留的資源配置。
  • 使用用法:
zincPower {
    // 這樣我們編譯出的apk中,只有 “默認” 和 “中文zh” 兩種資源
    resConfig "zh"
}

3.10 resConfigs(config)

  • 描述:保留的資源配置,和 resConfig 的區(qū)別在于,resConfigs 保留多個資源。
  • 使用用法:
zincPower {
    // 這樣我們編譯出的apk中,只有 “默認” 、 “中文zh” 和 “英文en” 兩種資源
    resConfigs "zh","en"
}

3.11 resValue(type, name, value)

  • 描述:添加 value 資源
  • 使用用法:
zincPower {
    // 添加至 res/value,通過 R.string.age 獲取
    resValue('string', 'age', '12year')
}

3.12 setConsumerProguardFiles(proguardFileIterable)

  • 描述:和 2.3小點 的功能一致,只是寫法不同,這里就不再贅述
  • 使用方法:
zincPower {
    consumerProguardFiles = [ 'consumer-rules.pro','zincPower-rules.pro' ]
}

3.13 setProguardFiles(proguardFileIterable)

  • 描述:添加混淆文件,和 2.13小點 的功能一致,只是寫法稍微不同,這里就不再贅述
  • 使用方法:
zincPower {
    proguardFiles = [getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro']
}

3.14 targetSdkVersion(targetSdkVersion)

  • 描述:應用的目標版本。說明我們已經(jīng)對指定的版本進行了測試,在開發(fā)過程中可以使用至該版本的API,否則會被提示無法使用。如果不設置,則和 minSdkVersion 的值保持一致。
  • 使用用法:
zincPower {
    targetSdkVersion 28
}

五、后記

Gradle 的配置文件看起來好像挺亂,其實是因為我們沒有進行整體的梳理,所謂 “無知最可怕”

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 在 Android Studio 構(gòu)建的項目中,基于 Gradle 進行項目的構(gòu)建,同時使用 Android DS...
    Ant_way閱讀 7,581評論 0 16
  • 因為我們發(fā)布或者推廣的渠道不同,就造成了我們的Android App可能會有很多個,因為我們需要細分他們,才能針對...
    acc8226閱讀 2,725評論 0 7
  • 1.介紹 如果你正在查閱build.gradle文件的所有可選項,請點擊這里進行查閱:DSL參考 1.1新構(gòu)建系統(tǒng)...
    Chuckiefan閱讀 12,355評論 8 72
  • Gradle是個構(gòu)建系統(tǒng),能夠簡化你的編譯、打包、測試過程。熟悉Java的同學,可以把Gradle類比成Maven...
    三也視界閱讀 1,017評論 0 2
  • Gradle構(gòu)建工具的使用 給Android 開發(fā)者的 Gradle 入門指南 https://www.oschi...
    better_huo閱讀 1,061評論 0 1

友情鏈接更多精彩內(nèi)容