之前在浏览掘金的时候,看到有大佬写过一篇文章关于Android ProductFlavor的文章,原文链接:
https://juejin.cn/post/6973570453629567012
但是由于之前在公司项目也用过ProductFlavor,发现和大佬用的有些区别,自己就硬着头皮去看完了官网的文档(英文不好的痛),原文地址
然后觉得应该记录下自己的学习历程
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
只有debug
和release
)
1.2 dimension组合配置
当一个module
中定义了不同的dimension
,并且在不同的Flavor
下使用,那么会组合使用,也就是总共会有 dimension【0】...dimension【n-1】(每个代表dimension
使用Flavor
个数,如果没有使用,就不需要计入)buldType的个数;其中配置会相互交叉
新建一个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个
2.提升使用
前面已经讲清楚如何使用dimension
配置,那么如何实现多渠道里面的不同配置,如app
名,applicationId
,icon
图标,甚至mainifest
下的配置参数呢。
2.1 defaultConfig配置修改
这就是ProductFlavor
下的第一个特点了,就是可以动态修改module
中defaultConfig
参数,包括:
applicationId
,minSdkVersion
,targetSdkVersion
,versionCode
,versionName
,javaCompileOptions
等配置
(具体有哪些可以查这个地址https://developer.android.google.cn/reference/tools/gradle-api/7.1/com/android/build/api/dsl/BaseFlavor?hl=en)
首先默认的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()]
}
}
}
这个配置应用的运行结果如下
接下来,就是使用ProductFlavor
的情况
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
环境下的包配置如下
再看看,product
环境下的包配置如下
完全生效的。
2.2 使用不同res/java目录替换配置
可以在app目录下根据不同的product,使用不同的java/res资源;然后根据不同的flavor,使用不同的配置
这里,更改两个参数,string
里面的app_name
这里举例更改:
原main环境下
dev
环境下
product
环境下
运行结果:
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
mainifest
文件
string.xml
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"
}
}
运行结果如下
2.4 buildConfigField 配置资源
在某些情况下,我们会需要根据不同的环境,加入以下不同的配置,这个也可以使用ProductFlavor中buildConfigField在编译时,动态配置。
buildConfigField具备三个参数 type(类型,这里可用基本类型),name(这个在BuildConfig中的名称),value(这个参数在BuildConfig中的值);具体参数解析如下
如下的配置参数
flavorDimensions "nation"
productFlavors{
dev{
dimension "nation"
//buildConfigField
buildConfigField "String", "TAG", '"apple"'
buildConfigField "int", "BASE_PORY", '12'
buildConfigField "Boolean", "IS_OK", 'true"
}
}
编译后 对应module的BuildConfig为
然后可以在代码中使用这些资源
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
;如果A
的ProductFlavor
下有 dev
和product
的flavor
,而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
dev
条件下是正常的
然而运行
product
就无法通过了
以上错误信息就是背景描述的情况;
其中
matchingFallbacks
格式如下
matchingFallbacks["子module使用flavor1","子module 的使用flavor2",...]
对应主module
下的flavor
在子module
会按照这个配置顺序去适配子module
的flavor
所以修改主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"
}
}
直接运行,运行告警
所以需要按照之前格式指定对应的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
missingDimensionStrategy https://kiranrao.in/blog/2020/03/31/gradle-missing-flavors/
项目地址: