最近加入了新公司,需要对已有项目改造使用Flavor实现多渠道打包。
项目是经典的多module依赖关系,最底层是common与网络,上层是app,中间是各种业务模块。
在使用flavor时遇到一个问题,因为除了多渠道之外,我还想把网络层也抽取出来,毕竟官方也是支持这种操作的嘛,flavorDimensions后面可以配置多个维度,比如我既需要多渠道,又需要多环境切换,那么就可以配置成:
android {
defaultConfig {
flavorDimensions "channel","environment"
}
productFlavors {
dev {
dimension "environment"
buildConfigField "String", "API_HOST", '"http://xxxxx/"'
}
prod {
dimension "environment"
buildConfigField "String", "API_HOST", '"http://xxxxx/"'
}
normal {
dimension "channel"
resValue "string", "app_name", "测试桌面"
}
szxcskj {
dimension "channel"
applicationIdSuffix ".szxcskj"
resValue "string", "app_name", "电视之家"
}
}
}
两个维度,一个表示渠道,一个表示网络环境,可以有dev,test,uat,prod等。
随后发现网络层是需要network模块下的,而只给network配置,不给其他模块配置环境,又会报错:
Cannot choose between the following variants of project :module_net:
- devDebugApiElements
- prodDebugApiElements
类似这样的错误,因为network配置了两个Flavor,而其他业务模块需要依赖网络层,但却不知道应该使用哪个flavor,所以报错。
虽然知道这个问题只需要把所有依赖的业务模块全部配置上对应的flavor就能解决,但是我们的业务模块有很多,而且我担心不便于后期维护,所以还是想找找其他方法。
在搜了很多资料,与chatGPT多轮对话,我是这样解决的。
先要学习两个东西:matchingFallbacks和missingDimensionStrategy。
我们假设A为主模块,B为副模块,A依赖B。
matchingFallbacks
适用场景:适合主模块想要以自己的某个flavor去匹配副模块的一个或多个flavor时。
场景:主副模块dimension相同,但主模块只有一个dev环境配置,而子模块有两个,需要主模块进行一个二选一。
A:flavor
dev
B:flavor
dev1
dev2
需要在A模块做以下配置:
flavorDimensions "env"
productFlavors{
dev{
dimension "env"
matchingFallbacks = ['dev1', 'dev2']
}
}
在dev中配置matchingFallbacks,表示如果在子模块中找不到当前Flavor(dev Flavor),则会去寻找子模块的dev1与dev2,优先匹配dev1。
missingDimensionStrategy
适用场景:主模块不想配置自己的flavor或者不想配置和子模块一样的dimension,或并不关心子模块使用哪个flavor。可以让子模块遇到该情况时,自动根据配置的优先顺序选择使用子模块flavor。
场景:主模块没有配置环境dimension,并不关心使用哪个环境,需要帮他指定一个优先级进行选择。
A:flavor
无
B:flavor
dev
test
需要在A模块做以下配置:
defaultConfig{
missingDimensionStrategy "env","dev","test"
}
在defaultConfig中配置missingDimensionStrategy,第一个参数是dimension,后续为优先级,如果子模块有dev和test,那么优先使用dev;如果子模块只有test,那么就使用test。
另一个案例:
场景:主模块配置了多个包,每个包使用的sdk版本不同,需要指定每个包使用哪个sdk版本。
productFlavors {
kukan {
dimension "channel"
resValue "string", "app_name", "酷看视频(sdk 1.0)"
buildConfigField "String", "KK_AD_APP_KEY", '"ebfff335250d4912b0118b0eb1ea5e02"'
missingDimensionStrategy "version", "standard"
}
youku {
dimension "channel"
applicationId "com.kukan.youku.demo"
resValue "string", "app_name", "优酷(sdk 3.0)"
buildConfigField "String", "KK_AD_APP_KEY", '"e90abd900c1a40139464af11fea7bfa0"'
missingDimensionStrategy "version", "high"
}
aqy {
dimension "channel"
applicationId "com.kukan.aqy.demo"
resValue "string", "app_name", "爱奇艺(sdk 3.0)"
buildConfigField "String", "KK_AD_APP_KEY", '"daa07b98b24547c0bf5830c8b9ba3f64"'
missingDimensionStrategy "version", "high"
}
txtv {
dimension "channel"
applicationId "com.kukan.txtv.demo"
resValue "string", "app_name", "腾讯视频(sdk 2.0)"
buildConfigField "String", "KK_AD_APP_KEY", '"4b31eefa15474d9a80c383dc88ba4127"'
missingDimensionStrategy "version", "low"
}
}
多个包需要使用不同的子模块的sdk版本,子模块做如下配置:
productFlavors {
standard {
dimension "version"
versionCode Integer.valueOf(VERSION_CODE)
versionName VERSION_NAME
buildConfigField "String", "VERSION_NAME", VERSION_NAME
buildConfigField "Integer", "VERSION_CODE", VERSION_CODE
}
low {
dimension "version"
versionName "2.0"
versionCode Integer.valueOf("2")
buildConfigField "String", "VERSION_NAME", '"2.0"'
buildConfigField "Integer", "VERSION_CODE", "2"
}
high {
dimension "version"
versionName "3.0"
versionCode Integer.valueOf("3")
buildConfigField "String", "VERSION_NAME", '"3.0"'
buildConfigField "Integer", "VERSION_CODE", "3"
}
}
参考资料:配置 build 变体 | Android 开发者 | Android Developers
总结:主模块和副模块flavor不相同或数量不同时,如何处理?
处理思路:
当主副模块的dimension相同时,应尽量使用matchingFallbacks方案,让自己的一个flavor去支持副模块的一个或多个flavor。
如果主副模块dimension不同,或主模块不关心子模块的flavor时,应使用missingDimensionStrategy的方案,可以忽视子模块的某个dimension下的flavor,并且为其设置优先级。