背景
随着 APP 的发展,以及业务的需求,有时我们需要提供代码业务逻辑相同,而页面展示的元素有部分差异的APP
,那我们如何快速的产出 APP
,打造 APP
产品矩阵?
目前 Google
官方已提供了构建变异体 APP
代码的能力,简单来说就是直接配置就可以了,并不需要我们处理太多东西;
基本步骤
这部分大同小异,没有什么需要说明的,直接看步骤即可
创建与 app/src/main
同级别的产品目录,里面保持与 main
里相同的目录结构
文件目录结构:
app gradle
中配置:
android {
compileSdkVersion 30
defaultConfig {
...
// 参数化打包配置
flavorDimensions "app"
}
// product 定义
productFlavors {
product_1 {
dimension "app"
applicationId = "com.egan.product_1"
}
product_2 {
dimension "app"
applicationId = "com.egan.product_2"
}
}
buildTypes {
release {
...
}
debug {
...
}
}
}
productFlavors
中定义名称需要与文件夹保持一致,这样会自动定位到我们的文件,而不用我们在进行配置文件路径
这样一个基本的构建 APP 变异体的配置就算基本完成了;我们仅仅处理了 applicationId 的差异;
当然我们也可以处理 versionName
、versionCode
等参数
我们除了在不同产品文件夹下处理资源文件以外,系统也提供了通过 脚本代码进行字符串等资源的写入
在编译时动态处理 res value
resValue "string", "AppName", "app1"
相当于在res/strings.xml 下增加一个名为 AppName
,值为 app1
的资源。在java中的使用 resValue
和strings.xml
的数据方式是一样的,context.getResources().getString(AppName);
在编译时动态处理 BuildConfig
buildConfigField "String", "AppName", "\"app1\"
java 代码中 BuildConfig.AppName
可以正常读取到写入的数据
在编译是动态处理 Manifest
manifestPlaceholders = [key: "value"]
在 manifest
中就可以通过 ${key}
的方式读取到数据,java
代码中可以通过读取 meta-data
来获取数据
以上算是基本配置构建变异体的基本步骤
下面说点不一样的:
大量参数的动态配置
我们如果需要处理少量的配置,这样配置问题不大,包括后续进行调整也并不麻烦;
痛点:
如何在构建的时候动态指定不同产品的 build
模式:
举个例子:
- 在
product_1
中,我需要区分debug
、release
环境 - 在
product_2
中,我需要区分debug
、release
环境 - 而
product_1
、product_2
中的debug
、release
环境的配置参数的值又不相同
那我如何动态的处理,保证构建出来的 apk 符合预期?
由于脚本的执行是顺序的,如果我们在配置 product 是又区分了 debug、release ,那你会发现,我们构建出来的 apk 里面配置的数据都是脚本中最后一个配置生效;
解决方案:
productFlavors.all { flavor ->
// flavor 是我们构建的变异体产品,这里可以配置 applicationId、versionName、versionCode 等数据
def flavorName = flavor.name
}
applicationVariants.all { variant ->
// variant 这里我们可以拿到
// 产品名称
def productName = variant.flavorName
// 打包类型
def buildType = variant.buildType.name
// 当拿到产品名称以及 打包类型的情况,就可以通过 外部配置文件,动态读取不同配置,通过 resValue 写入文件,项目中正常获取
}
比如测试项目的配置:
applicationVariants.all { variant ->
println("applicationVariants >>> ${variant.name}")
// 产品
def productName = variant.flavorName
// 打包类型
def buildType = variant.buildType.name
// 构建的特定产品参数
def buildProductParams = productParams.get(productName)
// 全部需要写入的参数
def buildParams = buildProductParams[buildType]
buildParams.each { k, v ->
// 需要写入数据
println("需要写入数据: $k : $v")
resValue("string", k, v)
}
}