目录
-
- 1.多渠道打包是什么
- 2.为什么需要多渠道打包
- 3.多渠道配置
- 3.构建变体组合
- 4.渠道过滤
- 5.渠道资源
- [6. 渠道依赖项](#6. 渠道依赖项)
- 7.渠道统计
- 8.管理渠道
1.多渠道打包是什么
多聚道打包指的是同一个应用生成多个不同的安装包,通常是APK文件,每个安装包可以包含不同的配置,资源。
2.为什么需要多渠道打包
1.数据统计:根据渠道区分来源,统计各个渠道的下载量和覆盖率;
2.精细化运营:根据数据分析来做营销和推广,提高应用的曝光
3.产品适配:需要适配不同厂商的系统API
3.多渠道配置
Variant
Variant是变体的意思,变体是指应用可以构建不同的版本,比如国内版和海外版,免费版和企业版等。
变体由多个构建类型组合而成,例如debug与release,以及构建脚本中定义的产品变种。
productFlavors
productFlavors翻译过来就是产品变种,用来定义不同的变体,每个变体可以有不同的配置和资源,最终打出来的包也不一样·
以华为、oppo为例子,在app/build.gradle文件中配置多渠道:
bash
flavorDimensions = ["version"]
productFlavors {
oppo {
dimension = 'version'
applicationId = 'com.example.gradlestudy.oppo'
versionName = '1.0'
versionCode = 1
}
huawei {
dimension = 'version'
applicationId = 'com.example.gradlestudy.huawei'
versionName = '1.0'
versionCode = 1
}
}
在productFlavors中通过create来创建渠道,并在渠道中按需配置属性参数。
比如applicationId所有渠道保持一致,这样可以保证一个设备只有一个应用安装,反正,如果想在一个设备上安装多个应用版本,也可以通过多渠道的方式来实现。
在build.gradle中defaultConfig{}中的配置为应用默认配置,都可以在渠道配置productFlavors{}中进行覆写和追加。
falvorDimensions表示产品变种的维度(Dimensions),是组的概念,同一个维度即为一个产品变种组,这里定义的是[version],名字可以自定义,也可以增加多个。
比如增加一个维度channel,比如要发布到Google Play和Amazon应用商店,这两个渠道可能有不同的品牌要求。
bash
flavorDimensions = ["version"]+["channel"]
productFlavors {
create("oppo") {
dimension = 'version'
applicationId = 'com.example.gradlestudy.oppo'
versionName = '1.0'
versionCode = 1
}
create("huawei") {
dimension = 'version'
applicationId = 'com.example.gradlestudy.huawei'
versionName = '1.0'
versionCode = 1
}
create("googlePlay") {
dimension "channel"
applicationId "com.example.gradlestudy.googleplay"
versionName = '1.0'
versionCode = 1
}
create("amazon") {
dimension "channel"
applicationId "com.example.gradlestudy.amazon"
versionName = '1.0'
versionCode = 1
}
}
每个维度的产品变种可以相互结合,生成不同的构建变体。例如,定义两个维度version和channel,每一个维度中包含两个产品风味,2*4=8的构建变体。
buildTypes
buildTypes是构建类型,用来定义构建类型配置,比如是否开启混淆,是否开启调试,通常包含debug和release两种类型。
bash
buildTypes {
getByName("debug") {
isDebuggable = true
}
getByName("release") {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
在多渠道配置中,构建类型和产品变种(productFlavors)一起使用,可以形成不同组合的构建变体(Variants)
3.构建变体组合
在配置完多渠道之后,打包方式跟平常的Run的有些区别,以前是单一的构建变体,现在是多组合的构建变体。
在AndroidStudio右侧打开Gradle面板,在build目录下可以看到具体的构建选项。
assemble是最全的构建方式,也就说所有的构建变体组合都会执行打包操作
assembleDebug会一次性打出4个包,比如执行assembleDebug会打出huaweiAmazonDebug,oppoAmazonDebug,huaweiGooglePlayDebug,oppoGooglePlayDebug
除了从Gradle面板选择任务执行之外,也可以使用命令行执行,例如:
kotlin
./gradlew assembleDebug
// or
./gradlew assembleHuawei
关于组合
现在的构建配置是:
- 2个维度(flavorDimensions): version,channel
- 4个产品变种(productFlavors):oppo,huawei,googlePlay,amazon
- 2个构建类型(buildTypes):debug,release
会构成2x4x2的组合
4.渠道过滤
在某些情况下,想在渠道构建中去掉某个渠道,这个时候可以创建变体过滤器来移除它。
java
variantFilter{
variant ->
def flavors = variant.getFlavors()*.name // 获取所有维度的风味名称列表
def buildTypeName = variant.buildType.name
println("names-------> ${variant.flavors*.name}----${variant.buildType.name}")
if(flavors.contains("googlePlay") && flavors.contains("oppo") && buildTypeName == "debug"){
println "忽略变体: ${flavors.join('-')}-${buildTypeName}"
setIgnore(true)
}
}
可以看到Build Variants中不会再显示oppoGooglePlayDebug变体
- 使用 variant.getFlavors() 获取所有维度的产品风味名称。
- 使用 variant.buildType.name 获取构建类型名称。
- 根据条件调用 setIgnore(true) 忽略不需要的变体。
5.渠道资源
随着市场多元化的发展,很多应用也出现了企业版,海外版,极速版等多种版本,对于渠道配置的定制诉求也越来越多,比如资源文件(文本、图片),以及代码
资源文件
以常见的App名称和Icon图标为例,默认在AndroidManifest.xml文件中的application标签中有如下配置:
java
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
>
</application>
</manifest>
android:icon和android:label分别引用的是app/src/main/目录下的mipmap图片资源和文本资源
这里的app/src/main目录为主目录,main相当于默认渠道,也可以新增其他渠道目录。
以huawei版为例子,在app/build.gradle文件中配置如下:
- res文件夹下新增values文件夹,values文件夹下新增strings.xml文件,并配置华为版的应用名称
- res文件夹下新增drawable和mipmap文件夹,并放置华为版的应用图标;

java
<resources>
<string name="app_name">GradleStudy huawei</string>
</resources>
这样在构建渠道变体华为的时候,Gradle会根据构建变体来找对应的目录文件,也就是说AndroidManifest.xml文件的android:icon和android:label引用的资源路径会从原来的main目录变为huawei目录。
在Build Variants中选择huaweiDebug变体
点击run查看运行效果
可以发现app应用图标和名字都换位huawei目录下对应的资源文件。
资源合并规则
- 渠道构建时,渠道变体(huawei)会跟主变体(main)目录下的资源进行合并
-如有同名配置资源,例如strings.xml文件中的app_name,则优先取渠道(huawei)配置资源进行覆盖,其他不同名的则进行合并; - layout文件、assets文件则是替换,渠道资源(huawei)优先于主变体(main)资源;
代码文件
代码文件不支持合并也不支持同名
比如在huawei渠道新增一个huaweiBean类,然后在main目录下也新增一个huaweiBean类,则会出现同名异常。
渠道中的代码为该渠道专属,只有在该渠道编译时才会与主变体main中的代码进行融合。
比如当切到oppoDebug渠道变体时,在main中就无法找到HuaweiBean类
如果oppo渠道想复用huawei渠道的代码可以使用sourceSets了。
SourceSets
sourceSets可以为渠道指定代码路径,以及res,manifest等资源文件路径。
如果oppo渠道想要复用huawei渠道的代码,我们就可以使用sourceSets来进行设置。
在app/build.gradle文件中配置如下:
java
sourceSets {
oppo {
res {
srcDirs 'src\\oppo\\res'
java.srcDirs("src/oppo/java", "src/huawei/java")
}
}
}
在oppo渠道配置中,指定代码路径,这样,在oppo渠道变体进行编译时,就会把src/huawei/java目录下的代码进行融合,从而实现复用huawei渠道代码的功能。
除了指定java,res目录下的代码文件之外,其他资源代码文件也是支持的,可以按需指定多个目录。
java
sourceSets {
oppo {
res {
java.srcDirs("src/oppo/java", "src/huawei/java")
kotlin.srcDirs("src/huawei/kotlin")
aidl.srcDirs("src/huawei/aidl")
res.srcDirs("src/huawei/res")
assets.srcDirs("src/huawei/assets")
jniLibs.srcDirs("src/huawei/jniLibs")
renderscript.srcDirs("src/huawei/rs")
manifest.srcFile("src/huawei/AndroidManifest.xml")
}
}
}
6. 渠道依赖项
除了资源文件和代码文件之外,我们的依赖可能会根据渠道有所不同,比如在做推送功能的时候,在打华为渠道包的时候,只依赖华为的推送,而不依赖oppo的推送,也就是根据渠道来配置依赖项。
当我们配置了渠道就会有对应的变体,[变体]+[依赖方式]就是渠道特有的依赖了。
以华为推送为例
java
dependencies {
huaweiImplementation("com.huawei.hms:push:6.11.0.300")
}
多渠道依赖方式:
- 默认依赖:implementation
- 渠道依赖:变体+Implementation,如huaweiImplementation
- 构建类型:类型+Implementation,如debugImplementation
- 组合变体:变体+类型+Implementation,如huaweiDebugImplementation
7.渠道统计
通过在打包前预置渠道信息,然后在运行时获取并上报,从而实现渠道的数据统计。
有两种方式
meta-data
meta-data标签通常在AndroidManifest.xml文件中使用,通过键值对的方式为组件提供附加配置信息。
常见的第三方渠道统计比如友盟,会在AndroidManifest.xml中使用标签通过占位符的方式,来存储渠道信息。
配置meta-data:
java
<meta-data android:name="UMENG_CHANNEL" android:value="${UMENG_CHANNEL_NAME}"/>
配置占位符:
java
productFlavors {
create("oppo") {
manifestPlaceholders["UMENG_CHANNEL_NAME"] = "oppo"
}
create("huawei") {
manifestPlaceholders["UMENG_CHANNEL_NAME"] = "huawei"
}
}
可以通过manifestPlaceholders来替换AndroidManifest.xml文件中的value的值,确保UMENG_CHANNEL_NAME要对应上。
获取渠道信息的方法:
通过PackageManager获取并读取meta-data信息
java
fun getChannel(context:Context):String?{
try {
val applicationInfo = context.packageManager.getApplicationInfo(context.packageName,PackageManager.GET_META_DATA)
if(applicationInfo.metaData!=null){
return applicationInfo.metaData.getString("UMENG_CHANNEL")
}
}catch (e:PackageManager.NameNotFoundException){
//处理异常
}
return null
}
BuildConfig
BuildConfig通常用来存储一些常量信息,比如版本号,或者在buildTypes中根据构建环境来定义接口请求的域名地址等,BuildConfig会在编译时生成class文件。实际上在配置多渠道信息的时候,已经默认在BuildConfig中注入渠道信息了。
获取渠道:
调用BuildConfig.FLAVOR
如果想要自定义渠道信息,比如增加渠道号,也可以通过BuildConfig来存储
在Gradle8 +版本中,需要开启BuildConfig功能
java
buildFeatures{
buildConfig = true
}
java
productFlavors {
create("oppo") {
buildConfigField("int","CHANNEL_CODE","1001")
}
create("huawei") {
buildConfigField("int","CHANNEL_CODE","1002")
}
}
配置完后重新运行就可以看到对应生成key的常量了
8.管理渠道
当渠道配置越来越多的时候,app目录下的build.gradle文件就会显得有些不易阅读和维护,这时候可以将配置模块化,把渠道相关配置抽成一个channel.gradle文件,然后在app/build.gradle文件中apply依赖进来,这样可以更好的管理和维护渠道项目的渠道配置,app/build.gradle文件也会少一些。
在项目根目录中新建channel.gradle文件,并配置如下:
java
android{
flavorDimensions = ["version"]
productFlavors {
create("oppo") {
dimension = 'version'
applicationId = 'com.example.gradlestudy.oppo'
versionName = '1.0'
versionCode = 1
manifestPlaceholders["UMENG_CHANNEL_NAME"] = "oppo"
buildConfigField("int", "CHANNEL_CODE", "1001")
}
create("huawei") {
dimension = 'version'
applicationId = 'com.example.gradlestudy.huawei'
versionName = '1.0'
versionCode = 1
manifestPlaceholders["UMENG_CHANNEL_NAME"] = "huawei"
buildConfigField("int", "CHANNEL_CODE", "1002")
}
}
sourceSets {
huawei {
res {
srcDirs 'src\\huawei\\res'
}
}
getByName("oppo") {
res {
srcDirs 'src\\oppo\\res'
//java.srcDirs("src/oppo/java", "src/huawei/java")
}
}
}
}
java
apply from:"../channel.gradle"
重新sync即可