ProductFlavor多渠道的神器

2021-06-22  本文已影响0人  诡异的叶子

之前在浏览掘金的时候,看到有大佬写过一篇文章关于Android ProductFlavor的文章,原文链接:
https://juejin.cn/post/6973570453629567012

但是由于之前在公司项目也用过ProductFlavor,发现和大佬用的有些区别,自己就硬着头皮去看完了官网的文档(英文不好的痛),原文地址

https://developer.android.google.cn/reference/tools/gradle-api/7.1/com/android/build/api/dsl/ProductFlavor

然后觉得应该记录下自己的学习历程

image.png

1.ProductFlavor的简单使用

1.1 dimension简单使用

首先,是在module的目录下(可以是application,library)中build.gradle中,配置信息如下

 flavorDimensions "nation"
    productFlavors{
        dev{
            dimension "nation"
        }
        product{
            dimension "nation"       
        }

        user{
            dimension "nation"
        }
    }

其中,productFlavors下每个flavor都必须有一个dimension(Android Gradle Plugin 3.0.0以后引入);这样build variant下就有如下几个编译条件(这里假设buildType只有debugrelease

image.png

1.2 dimension组合配置

当一个module中定义了不同的dimension,并且在不同的Flavor下使用,那么会组合使用,也就是总共会有 dimension【0】...dimension【n-1】(每个代表dimension使用Flavor个数,如果没有使用,就不需要计入)buldType的个数;其中配置会相互交叉

image.png

新建一个module,其中ProductFlavor的配置如下

flavorDimensions 'api', 'version'

    productFlavors {
        demo {
            dimension 'version'
        }

        full {
            dimension 'version'

        }

        minApi24 {
            dimension 'api'
        }

        minApi21 {
            dimension "api"
        }
    }

那么build variant的环境会有:

配置个数= api的flavor个数(2)* version的flavor个数*buildType个数(2) = 8个

image.png

2.提升使用

前面已经讲清楚如何使用dimension配置,那么如何实现多渠道里面的不同配置,如app名,applicationIdicon图标,甚至mainifest下的配置参数呢。

2.1 defaultConfig配置修改

这就是ProductFlavor下的第一个特点了,就是可以动态修改moduledefaultConfig参数,包括:
applicationIdminSdkVersiontargetSdkVersionversionCodeversionNamejavaCompileOptions等配置
(具体有哪些可以查这个地址https://developer.android.google.cn/reference/tools/gradle-api/7.1/com/android/build/api/dsl/BaseFlavor?hl=en

image.png

首先默认的defaultConfig配置

 defaultConfig {
        applicationId "com.example.myandroidkotlin"
        minSdkVersion 18
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        multiDexEnabled true

        missingDimensionStrategy 'consumer', 'consumed'

        javaCompileOptions {
            annotationProcessorOptions {
                arguments += ["room.schemaLocation":
                                      "$projectDir/schemas".toString()]
            }
        }
    }

这个配置应用的运行结果如下

image.png

接下来,就是使用ProductFlavor的情况

image.png
flavorDimensions "nation"
    productFlavors{
        dev{
            dimension "nation"
            //defaultconfig
            applicationId "com.example.myandroidkotlin.dev"
            minSdkVersion 18
            targetSdkVersion 30
            versionCode 101
            versionName "1.0.1"
        }
        product{
            dimension "nation"
            //defaultconfig
            applicationId "com.example.myandroidkotlin.product"
            minSdkVersion 16
            targetSdkVersion 29
            versionCode 120
            versionName "1.2.0"
        }
    }

首先,dev环境下的包配置如下

image.png

再看看,product环境下的包配置如下

image.png

完全生效的。

2.2 使用不同res/java目录替换配置

可以在app目录下根据不同的product,使用不同的java/res资源;然后根据不同的flavor,使用不同的配置

image.png

这里,更改两个参数,string里面的app_name

这里举例更改:
原main环境下

image.png image.png

dev环境下

image.png image.png

product环境下

image.png image.png

运行结果:

image.png

2.3 manifestPlaceholders 向mainifest注入资源

在某些情况下,会需要根据不同的配置,更改mainifest下的部分参数配置,比如app的名字或者icon,这里就需要使用manifestPlaceholders,如果想看官网原文,连接如下,https://developer.android.google.cn/studio/build/manifest-build-variables.html?hl=en
这里建议 和2.2使用不同res/java目录替换配置二选一,只使用其中一种

同样的,可以使用manifestPlaceholders实现上面一样的效果;

这里需要注意,flavor中使用mainifestPlaceholders一定要指定ENVIRONMENT,默认环境为main(也就是资源目录下main文件)
引入新的icon

image.png

mainifest文件

image.png

string.xml

image.png

build.gradle 配置信息

 flavorDimensions "nation"
    productFlavors{
        dev{
            dimension "nation"
            manifestPlaceholders=[ENVIRONMENT:"main",
                                  app_icon: "@mipmap/newlogo",
                                  app_name:"@string/app_name_dev"]
        }
        product{
            dimension "nation"
            manifestPlaceholders=[ENVIRONMENT:"main",
                                  app_icon: "@mipmap/newlogo",
                                  app_name:"@string/app_name_product"
        }
    }

运行结果如下

image.png

2.4 buildConfigField 配置资源

在某些情况下,我们会需要根据不同的环境,加入以下不同的配置,这个也可以使用ProductFlavor中buildConfigField在编译时,动态配置。

buildConfigField具备三个参数 type(类型,这里可用基本类型),name(这个在BuildConfig中的名称),value(这个参数在BuildConfig中的值);具体参数解析如下

image.png

如下的配置参数

 flavorDimensions "nation"
    productFlavors{
        dev{
            dimension "nation"
            //buildConfigField
            buildConfigField "String", "TAG", '"apple"'
            buildConfigField "int", "BASE_PORY", '12'
            buildConfigField "Boolean", "IS_OK", 'true"

        }
      }

编译后 对应module的BuildConfig为

image.png

然后可以在代码中使用这些资源

2.5 matchingFallbacks和missingDimensionStrategy

这两个特性当时刚看的时候,看的头大,而且官网资料写的很少,最终在stackoverflow看到有人推荐的一篇博文,然后自己试着写了一些测试代码,终于是搞定了。文章地址https://kiranrao.in/blog/2020/03/31/gradle-missing-flavors/

2.5.1 matchingFallbacks

首先说matchingFallbacks的设计背景:在多module依赖的模式下,如module A依赖 module B ;如果AProductFlavor下有 devproductflavor,而B中只有dev;那么当mudule A编译devXXX的情况下,会正常通过编译;而编译product的情况下,会异常;理由就是B中找不到对应的product 条件的flavor;而matchingFallbacks就是用来解决这个问题的

上面说这么多,不如直接代码来演示
新建一个module名字叫flavor1,它build.gradle内容如下:

flavorDimensions "nation"
    productFlavors{
        dev{
            dimension "nation"
        }
    }

然后app module依赖flavor1,它build.gradle 内容如下:

 flavorDimensions "nation"
    productFlavors{
        dev{
            dimension "nation"
        }
        product{
            dimension "nation"
        }
    }

这里 主module有 dev,product两个flavor;子module只有一个dev 的flavor

image.png

dev条件下是正常的

image.png
然而运行product就无法通过了 image.png
以上错误信息就是背景描述的情况;
其中matchingFallbacks格式如下

matchingFallbacks["子module使用flavor1","子module 的使用flavor2",...]

对应主module下的flavor在子module会按照这个配置顺序去适配子moduleflavor

所以修改主module配置(matchingFallbacks

 flavorDimensions "nation"
    productFlavors{
        dev{
            dimension "nation"
        }
        product{
            dimension "nation"
             //matchingFallbacks 多module匹配条件
            matchingFallbacks = ['dev']
        }
    }

然后再次编译安装,ok了;总结一下,这里当子module有的flavor,而主module存在的flavor,(dimension需要一样)就需要配置matchingFallbacks使用子mudule中的flavor顺序。

2.5.2 missingDimensionStrategy

使用条件:当主module中不存在,而子module中存在的dimension,就需要在主module中定义使用哪个dimension下的哪一个flavor;其结构如下:

missingDimensionStrategy["dimension","子module dimension 下的使用flavor"]

首先,创建子module flavor2,其build.gradle下,主module依赖它

 flavorDimensions "nations"
    productFlavors{
        dev{
            dimension "nations"

        }
        product{
            dimension "nations"
        }

        user{
            dimension "nations"
        }
    }

直接运行,运行告警


cbae56292f318b7203ee2b052c6a7d8.png

所以需要按照之前格式指定对应的dimension 使用子module中哪一个flavor
所以这里可以在主 module配置

 flavorDimensions "nation"
    productFlavors{
        dev{
            dimension "nation"         
            missingDimensionStrategy 'nations', 'dev'
        }
        product{
            dimension "nation"         
            missingDimensionStrategy 'nations', 'product'

        }

完成配置运行成功

参考

尾巴大不掉 https://juejin.cn/post/6973570453629567012?utm_source=gold_browser_extension

google官网 https://developer.android.google.cn/reference/tools/gradle-api/7.1/com/android/build/api/dsl/ProductFlavor

missingDimensionStrategy https://kiranrao.in/blog/2020/03/31/gradle-missing-flavors/

项目地址:

https://github.com/jiaoery/MyAndroidKotlin

上一篇下一篇

猜你喜欢

热点阅读