
目录
- 背景介绍
- productFlavors介绍
- productFlavors应用
- 参考
- 后续...
背景介绍
在平时开发过程中,一般会有多个环境,像开发环境、测试环境、预生产环境等等。同时在你网络的配置文件里面可能是这样的写的:
//public static String DOMAIN_HOST = "https://iam.dev.com/";//开发环境
public static String DOMAIN_HOST = "https://iam.test.com/";//测试环境
//public static String DOMAIN_HOST = "https://iam.pre.com/";//预生产环境
//public static String DOMAIN_HOST = "https://iam.production.com/";// 正式环境
配置一个全局的静态变量host,把每一种环境都写上,需要哪个环境的就把哪个环境的注释放开,然后去打包。
可能会遇到下面的场景👇:
测试:“你帮我装一个测试环境的包可以吗?”
你:“可以”
接着你打开了网络配置文件,看了一眼,嗯,正好是测试环境,插上手机,Run APP,运行成功,愉快的给了测试,继续埋头写bug,过了一会儿后台的兄弟来了,他后台改了一点东西,想在手机上试试数据好不好使,然后有了下面的对话👇:
后台兄弟:“帮我装个包,我还没同步部署到测试环境呢,你先帮我装个开发环境的吧”
你:“可以”
接着你打开了网络配置文件,看了一眼,刚才给测试跑的测试环境,现在得切换来
public static String DOMAIN_HOST = "https://iam.dev.com/";//开发环境
//public static String DOMAIN_HOST = "https://iam.test.com/";//测试环境
//public static String DOMAIN_HOST = "https://iam.pre.com/";//预生产环境
//public static String DOMAIN_HOST = "https://iam.gradle.com/";// 正式环境
然后插上手机,Run APP,运行成功,打开app一看,嗯?版本号怎么是0.5呢?在开发环境应该是1.0才对啊,哦,想起来了,测试环境和开发环境的版本号不一样,得去build.gradle文件里面改改,打开build.gradle
android{
...
defaultConfig {
...
minSdkVersion 19
targetSdkVersion 28
//开发环境
// versionCode 10
// versionName "1.0"
//测试环境
versionCode 5
versionName "0.5"
//正式环境
// versionCode 3
// versionName "0.3"
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"//, "armeabi"
}
...
}
...
}
果然,用的是测试环境的版本号。那改一下吧,把测试环境的注释掉,把开发环境的放开
...
//开发环境
versionCode 10
versionName "1.0"
//测试环境
//versionCode 5
//versionName "0.5"
....
改好了,之后再运行 Run App,成功!给后台兄弟。
(假如)有一天,测试和后台兄弟一起来找你:
测试:“前几天的bug你们都改完了,我这边需要回归测试,你再给我打一个测试环境的包吧...”
后台兄弟:“我这边又有新的改动了,之前的app被我删了,你再帮我装一个开发环境的包吧...”
你:“测试你先等等哈,我在给后台兄弟打包(关系好)”
修改网络配置文件,修改build.gradle文件,Run App,成功。给后台兄弟装好了,
修改网络配置文件,修改build.gradle文件,Run App,成功。给测试装好了。
来回切换,来回注释烦人不?现在是两个环境,如果有更多的环境和版本是不是更头大,咋办呢?
productFlavors介绍
从单词上来看,可以称为“产品风味”,官方文档给出的翻译是“产品变体”,不管怎么翻译,从这个词语可以窥到这个关键词的代码块可以修改项目的一些属性。为了更好理解,我把不加productFlavors的项目看做是一瓶纯净水,而productFlavors就像是调味工具一样,可以让一个项目拥有各种各样的味道,至于想要什么味道,取决于你。
先看一下怎么声明它👇:
android {
...
defaultConfig {...}
buildTypes {
debug{...}
release{...}
}
// Specifies one flavor dimension.
flavorDimensions "milkytea"
productFlavors {
warm {
dimension "milkytea"
}
cool{
dimension "milkytea"
}
}
}
根据规定,在声明productFlavors前,必须要指定一个维度,让这个“产品风味”有一个分组,还拿我们纯净水举例,我们想让纯净水变成奶茶,奶茶有常温的、有凉的,在这里不管是常温的还是凉的,都属于奶茶,奶茶就属于一个维度,一个分组。
在build.gradle文件中添加好productFlavors,Sync一下项目,你就可以发现,在Build Variants中已经可以看到我们项目已经分成了四个版本
分别是coolDebug、coolRelease、warmDebug、warmRelease,这四个版本怎么来的呢?这里是因为productFlavors会结合构建类型(buildTypes)里面的类型一起创建,可能你看到上图中的buildTypes中只有一个release版本,那怎么同时也生成debug版本呢?
引用官网的一段话解释就是:
当您创建新模块时,Android Studio 会自动为您创建“debug”构建类型和“release”构建类型。虽然“debug”构建类型没有出现在构建配置文件中,但 Android Studio 会使用 debuggable true 配置它。这样,您就可以在安全的 Android 设备上调试应用,并使用常规调试密钥库配置 APK 签名。
就是说Android studio会利用 debuggable属性为我们生成好一个debug版本的代码块,这样结合productFlavors就会出现四个风味不一样的版本。
奶茶除了有常温的、凉的这些种类之外,我们还能在奶茶里添加更种东西,接着我们添加一点佐料,让奶茶更好喝。
android {
...
defaultConfig {...}
buildTypes {
debug{...}
release{...}
}
// Specifies one flavor dimension.
flavorDimensions "milkytea",'condiments'
productFlavors {
warm {
dimension "milkytea"
}
cool{
dimension "milkytea"
}
sugar{
dimension 'condiments'
}
ice{
dimension 'condiments'
}
}
}
可以看到在flavorDimensions分组上我们又添加了一个佐料(condiments)的分组,同时在productFlavors里面添加了糖(suger)和冰(ice),因为这两样东西都属于佐料,所以指定分组就是condiments,这时候再同步一下项目,就可以看到我们的奶茶又变成了不同的口味:
在Build Variants中中就可以看到变成了八个风味,分别是:
coolIceDebug、
coolIceRelease、
coolSugerDebug、
coolSugerRelease、
warmIceDebug、
warmIceRelease、
warmSugerDebug、
warmSugerRelease
所以可以看出Gradle利用以下命名方式去构建不同风格的变体:
[cool, warm][ice, suger][Debug, Release]
对应的apk就是:
app-[cool, warm][ice, suger][Debug, Release].apk
如果够细心你会发现一个问题,在八个风味的奶茶里面,有两个风味是warmIceDebug、warmIceRelease。常温加冰?这不就是凉的吗,在现实中我们可以要求店员去除这种情况,那在Gradle中,应该怎么过滤这种情况呢?往下看👇:
android {
...
defaultConfig {...}
buildTypes {
debug{...}
release{...}
}
// Specifies one flavor dimension.
flavorDimensions "milkytea",'condiments'
productFlavors {
warm {
dimension "milkytea"
}
cool{
dimension "milkytea"
}
sugar{
dimension 'condiments'
}
ice{
dimension 'condiments'
}
}
variantFilter { variant ->
def names = variant.flavors*.name
if (names.contains("warm") && names.contains("ice")) {
setIgnore(true)
}
}
}
在productFlavors代码块下面,添加variantFilter,就是变体的过滤器,通过名字作为判断条件,来过滤掉不想要的变体,不会影响到其他变体的组合,再同步一下项目可以看到:
只剩下了6种组合方式,warmIceDebug、warmIceRelease两种不合理的搭配方式已经被过滤掉了。
“你上面说这么一大堆,有什么用?”
“又是奶茶,又是加冰的,后台跟测试找我,不还是得来回切换环境吗,跟这个有啥关系”
“奶茶也可以加珍珠、布丁...”
“想喝...”
你可能有点不耐烦了,现在并没有解决我来回切环境的苦恼,甚至跟配置host什么没有一点关系 “你说的我都懂,可是鸽子为什么那么...”
“说错了... 可是这个东西究竟该解决上面👆切换环境的问题?”
往下看👇:
productFlavors应用
从productFlavors代码块中可以看到,在每一种变体里面我们只是给它们指定了一个分组,并没有写其他的信息,如果想配置每个版本对应host的话,我们可以这样写:
android {
...
defaultConfig {...}
buildTypes {
debug{...}
release{...}
}
// Specifies one flavor dimension.
flavorDimensions 'version'
productFlavors{
dev{
dimension 'version'
//三个参数:1.要定义的常量的类型 2.该常量的命名 3.该常量的值
buildConfigField "String", "HOST", "https://iam.dev.com/"
}
Atest{//因为test是关键字,所以换了一个名字
dimension 'version'
buildConfigField "String", "HOST", "https://iam.test.com/"
}
pre{
dimension 'version'
buildConfigField "String", "HOST", "https://iam.pre.com/"
}
production{
dimension 'version'
buildConfigField "String", "HOST", "https://iam.production.com/"
}
}
}
因为只是配置host相关,所以一个分组就可以满足我们的需求,另外可以看到我们在每一个变种里面添加了一个buildConfigField,这个字段用于给BuildConfig文件添加一个字段,BuildConfig文件在build文件夹下可以找到,在这里我们不过多的介绍,有时间再单独说,这里只说怎么用。
添加好以后,同步项目,在我们的网络配置文件中就可以这样写了:
public static String DOMAIN_HOST = BuildType.HOST;
BuildType.HOST获取到值是随着你变种中的值一起改变的,也就是说你在Build Variants中选择哪一个变种,获取到就是哪一个变种里面配置的buildConfigField所对应的值,这样你再去打包的时候,只需要在Build Variants选择一下你想要的版本就可以了,不用再来回注释,网络配置文件也不用改动。
“那个测试环境和开发环境版本不一致的问题怎么解决?”
也是一样的操作,还是在变体中配置就好,之前我们的versionCode和versionName都是写在defaultConfig代码块里面
现在我们提出来,写在变体的代码块中。根据环境配置不同的版本:
android {
...
defaultConfig {...}
buildTypes {
debug{...}
release{...}
}
// Specifies one flavor dimension.
flavorDimensions 'version'
productFlavors{
dev{
dimension 'version'
//三个参数:1.要定义的常量的类型 2.该常量的命名 3.该常量的值
buildConfigField "String", "HOST", "https://iam.dev.com/"
versionCode 10
versionName "1.0"
}
Atest{
dimension 'version'
buildConfigField "String", "HOST", "https://iam.test.com/"
versionCode 5
versionName "0.5"
}
pre{
dimension 'version'
buildConfigField "String", "HOST", "https://iam.pre.com/"
versionCode 3
versionName "0.3"
}
production{
dimension 'version'
buildConfigField "String", "HOST", "https://iam.production.com/"
versionCode 3
versionName "0.3"
}
}
}
这样,每个变体都有可对应的版本号和版本名称,切换方式同样是利用Build Variants,每个版本和变体一一对应,再也不用每次都注释一个,放开一个,再同步了。不管有再多的环境、再多的版本,配置好productFlavors,就可以自由的切换啦。
参考
使用gradle的productFlavors实现Android项目多渠道打包
后续
后续会再补充变体打包的一些注意事项和便捷方式,今天先到这里。