Android核心技术深入浅出AndroidJava 核心技术

Gradle中Flavor使用

2021-07-26  本文已影响0人  安卓技术砖家

一、前言

本文主要分享Gradle中两个参数flavorDimensions和productFlavors,而这两个参数成对出现,可以做一些差分化定义。需要事先说明的是,接下来所说的 “意义” 并非官方文档翻译,而是结合了自己的理解,会比较口语化。


二、flavorDimensions 的意义

flavorDimensions从单词字面理解知道是 “风味维度”,是需要结合 “产品风味(即productFlavors)” 来一起使用的。flavorDimensions的使用会定义出维度,供接下来的 productFlavors 使用。我们举个例子

android {
    // 省略其他参数
    flavorDimensions('abi', 'version')
}

使用上面代码,则会定义出两个维度:version和abi。一个参数一个维度,我们把它形象化,就可以看成下面这样一张图。而他有什么作用,我们看下一小节。

安卓_AS中Flavor使用_内容1.png

如果三个参数就可以看成一个三维的空间坐标系,这里的坐标系只是一个形象化的体现。


三、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”。

安卓_AS中Flavor使用_内容2.png

而这些维度的交织就会形成最终的风味,即我们上面所标出来的 “armV7V1”、“armV7V2”、“armV7V3”、“x86V1”、“x86V2”、“x86V3”。我们可以根据不同的风味,打出不同的apk包,便可以实现一套核心代码打出多个有些差异的包。我们先了解下它可配置的参数。


四、productFlavor

productFlavors 下的每个项最终形式是 productFlavor,即我们上面说所的 “v1”,“v2”…

1. productFlavor 存在形式

我们知道,每个配置最终会被映射为一个类,或是一个属性、或一个方法。productFlavor 也不例外,他会被映射为com.android.build.gradle.internal.dsl.ProductFlavor,继承结构如下

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

zincPower {
    // applicationId 应用的包名,会覆盖 defaultConfig 中的 applicationId
    // applicationId 会替换 AndroidManifest.xml 中的 manifest 标签下 package 的 value
    applicationId "com.zinc.power"
    ...
}

2.2 applicationIdSuffix

zincPower {
    applicationIdSuffix '.debug'
}

2.3 consumerProguardFiles

zincPower {
    consumerProguardFiles 'consumer-rules.pro'
    ......省略其他配置
}
// 因为该属性是一个 List<File> 类型,如果需要多个文件配置,则如下所示
zincPower {
    consumerProguardFiles 'consumer-rules.pro','zincPower-rules.pro'
    ......省略其他配置
}

2.4 dimension

ERROR: Flavor 'v1' has no flavor dimension.
zincPower {
    // 关联纬度
    dimension 'version'
    ......省略其他配置
}

2.5 externalNativeBuild

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

zincPower {
    javaCompileOptions {
        annotationProcessorOptions{
            arguments = []
            classNames ''
            ....
        }
    }
    ......省略其他配置
}

2.7 manifestPlaceholders

我们配置差异化的 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使用_内容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使用_内容5.png

2.8 matchingFallbacks

举个例子:
我们有一个 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'
        }
    }
}

2.9 multiDexEnabled

2.10 multiDexKeepFile

zincPower {
    multiDexKeepFile file('multidex-config.txt')
    ...
}

multidex-config.txt 中的书写则如下,每一个文件则为一行

com/example/MyClass.class
com/example/TestClass.class

2.11 multiDexKeepProguard

zincPower  {
    multiDexKeepFile file('multidex-config.pro')
    ...
}

multidex-config.pro 中的写法如下

// 将会保留所有的在com.example package的类
-keep class com.example.** { *; }

2.12 ndk

zincPower {
    // ndk中,目前只有 abiFilter 一个属性,所以 ndk 目前来说只用于 abi 的过滤
    ndk {
        abiFilter 'armeabi-v7a'
    }
    ...
}

2.13 proguardFiles

zincPower {
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}

2.14 signingConfig

  1. 我们需要先在项目根目录下创建一个 keystore.properties 文件,在文件中写入对应的配置,如图所示
安卓_AS中Flavor使用_内容6.png
  1. 在应用级的 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

zincPower {
    vectorDrawables {
        // 如果 minSdkVersion 小于 21,只生成mdpi的png
        generatedDensities 'mdpi'
        // 设置为 true,会忽略 generatedDensities ,会加入svg兼容包,不会再产生png
        useSupportLibrary true
    }
}

2.16 versionCode

zincPower {
    versionCode 1000
    ......
}

2.17 versionName

zincPower {
    versionName "1.0.0"
    .....
}

2.18 versionNameSuffix

zincPower {
    // 如果 versionName "1.0.0" ,则最终的版本名为 1.0.0.test
    versionNameSuffix ".test"
    .....
}

3. productFlavor 的方法意义

3.1 buildConfigField(type, name, value)

// 值的注意的是 value 的值是原样放置,我们通过使用方法一节来了解
<type> <name> = <value>
productFlavors {
    x86 {
        // 可以通过 BuildConfig 进行获取
        buildConfigField('String', 'name', '"XiaoPenYou"')
        buildConfigField('int', 'age', '26')
        .....
    }
}

最终会生成如下图的配置,我们可以通过下面代码进行获取

String name = BuildConfig.name;
int age = BuildConfig.age;
安卓_AS中Flavor使用_内容7.png

值的一提的是,我们设置 String 类型的参数时,需要加上 “” 双引号(如例子中的name属性)。切记!

3.2 consumerProguardFile(proguardFile)

zincPower {
    consumerProguardFile('consumer-rules.pro')
}

3.3 consumerProguardFiles(proguardFiles)

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)

我们的项目结构如下

安卓_AS中Flavor使用_内容8.png

zinclibrarybuild.gradle 中编写了如下渠道配置

// 创建 风味维度
flavorDimensions('zinc', 'handsome')
// 创建产品风味
productFlavors {
    minApi13{
        dimension 'zinc'
    }
    minApi23{
        dimension 'zinc'
    }
    x86{
        dimension 'handsome'
    }
    arm64{
        dimension 'handsome'
    }
}

此时如果直接在 appbuild.gradle 中添加依赖,同步时便会出错

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

所以我们需要在 appbuild.gradle 中使用这个参数进行忽略 library 中带来的维度和风味,即使用如下代码

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

3.7 proguardFile(proguardFile)

zincPower {
    proguardFile 'proguard-rules.pro'
}

3.8 proguardFiles(files)

debug {
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}

3.9 resConfig(config)

zincPower {
    // 这样我们编译出的apk中,只有 “默认” 和 “中文zh” 两种资源
    resConfig "zh"
}

3.10 resConfigs(config)

zincPower {
    // 这样我们编译出的apk中,只有 “默认” 、 “中文zh” 和 “英文en” 两种资源
    resConfigs "zh","en"
}

3.11 resValue(type, name, value)

zincPower {
    // 添加至 res/value,通过 R.string.age 获取
    resValue('string', 'age', '12year')
}

3.12 setConsumerProguardFiles(proguardFileIterable)

zincPower {
    consumerProguardFiles = [ 'consumer-rules.pro','zincPower-rules.pro' ]
}

3.13 setProguardFiles(proguardFileIterable)

zincPower {
    proguardFiles = [getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro']
}

3.14 targetSdkVersion(targetSdkVersion)

zincPower {
    targetSdkVersion 28
}

五、后记

Gradle 的配置文件看起来好像挺乱,其实是因为我们没有进行整体的梳理,所谓 “无知最可怕”

上一篇下一篇

猜你喜欢

热点阅读