当前位置: 首页 > news >正文

Android Gradle多渠道打包

目录

    • 1.多渠道打包是什么
    • 2.为什么需要多渠道打包
    • 3.多渠道配置
      • Variant
      • productFlavors
      • buildTypes
    • 3.构建变体组合
      • 关于组合
    • 4.渠道过滤
    • 5.渠道资源
      • 资源文件
      • 资源合并规则
      • 代码文件
      • SourceSets
    • 6. 渠道依赖项
    • 7.渠道统计
      • meta-data
      • BuildConfig
    • 8.管理渠道

1.多渠道打包是什么

多聚道打包指的是同一个应用生成多个不同的安装包,通常是APK文件,每个安装包可以包含不同的配置,资源。

2.为什么需要多渠道打包

1.数据统计:根据渠道区分来源,统计各个渠道的下载量和覆盖率;
2.精细化运营:根据数据分析来做营销和推广,提高应用的曝光
3.产品适配:需要适配不同厂商的系统API

3.多渠道配置

Variant

Variant是变体的意思,变体是指应用可以构建不同的版本,比如国内版和海外版,免费版和企业版等。
变体由多个构建类型组合而成,例如debug与release,以及构建脚本中定义的产品变种。

productFlavors

productFlavors翻译过来就是产品变种,用来定义不同的变体,每个变体可以有不同的配置和资源,最终打出来的包也不一样·

以华为、oppo为例子,在app/build.gradle文件中配置多渠道:

    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应用商店,这两个渠道可能有不同的品牌要求。

 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两种类型。

    buildTypes {getByName("debug") {isDebuggable = true}getByName("release") {isMinifyEnabled = falseproguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")}}

在多渠道配置中,构建类型和产品变种(productFlavors)一起使用,可以形成不同组合的构建变体(Variants)
在这里插入图片描述

3.构建变体组合

在配置完多渠道之后,打包方式跟平常的Run的有些区别,以前是单一的构建变体,现在是多组合的构建变体。

在AndroidStudio右侧打开Gradle面板,在build目录下可以看到具体的构建选项。
在这里插入图片描述
assemble是最全的构建方式,也就说所有的构建变体组合都会执行打包操作
assembleDebug会一次性打出4个包,比如执行assembleDebug会打出huaweiAmazonDebug,oppoAmazonDebug,huaweiGooglePlayDebug,oppoGooglePlayDebug
在这里插入图片描述
除了从Gradle面板选择任务执行之外,也可以使用命令行执行,例如:

./gradlew assembleDebug
// or 
./gradlew assembleHuawei

关于组合

现在的构建配置是:

  • 2个维度(flavorDimensions): version,channel
  • 4个产品变种(productFlavors):oppo,huawei,googlePlay,amazon
  • 2个构建类型(buildTypes):debug,release

会构成2x4x2的组合

4.渠道过滤

在某些情况下,想在渠道构建中去掉某个渠道,这个时候可以创建变体过滤器来移除它。

    variantFilter{variant ->def flavors = variant.getFlavors()*.name // 获取所有维度的风味名称列表def buildTypeName = variant.buildType.nameprintln("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标签中有如下配置:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><applicationandroid: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文件夹,并放置华为版的应用图标;

在这里插入图片描述

<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文件中配置如下:

    sourceSets {oppo {res {srcDirs 'src\\oppo\\res'java.srcDirs("src/oppo/java", "src/huawei/java")}}}

在oppo渠道配置中,指定代码路径,这样,在oppo渠道变体进行编译时,就会把src/huawei/java目录下的代码进行融合,从而实现复用huawei渠道代码的功能。
除了指定java,res目录下的代码文件之外,其他资源代码文件也是支持的,可以按需指定多个目录。

    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的推送,也就是根据渠道来配置依赖项。
当我们配置了渠道就会有对应的变体,[变体]+[依赖方式]就是渠道特有的依赖了。
以华为推送为例

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:

       <meta-data android:name="UMENG_CHANNEL" android:value="${UMENG_CHANNEL_NAME}"/>

配置占位符:

    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信息

    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功能

    buildFeatures{buildConfig = true}
    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文件,并配置如下:

android{flavorDimensions = ["version"]productFlavors {create("oppo") {dimension = 'version'applicationId = 'com.example.gradlestudy.oppo'versionName = '1.0'versionCode = 1manifestPlaceholders["UMENG_CHANNEL_NAME"] = "oppo"buildConfigField("int", "CHANNEL_CODE", "1001")}create("huawei") {dimension = 'version'applicationId = 'com.example.gradlestudy.huawei'versionName = '1.0'versionCode = 1manifestPlaceholders["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")}}}
}
apply from:"../channel.gradle"

重新sync即可

相关文章:

  • 基于 Vue3 + ECharts + GeoJson 实现区域地图钻取功能详解
  • WEMOS LOLIN32 开发板引脚布局和技术规格
  • 25.4.20学习总结
  • PyTorch基础学习系列一
  • git学习日志
  • 从跌倒到领跑:北京亦庄机器人马拉松如何改写人机协作未来?
  • 深度解析微前端架构设计:从monorepo工程化设计到最佳实践
  • 云效部署实现Java项目自动化部署图解
  • 飞帆平台 cdn 资源集锦
  • 详解trl中的GRPOTrainer和GRPOConfig
  • DeepSeek R1 7b,Langchain 实现 RAG 知识库 | LLMs
  • 初级达梦dba的技能水准
  • 绝对路径与相对路径
  • uniapp-商城-29-vuex 关于系统状态的管理
  • AIGC-几款医疗健康智能体完整指令直接用(DeepSeek,豆包,千问,Kimi,GPT)
  • 8节串联锂离子电池组可重构buck-boost均衡拓扑结构 simulink模型仿真
  • 【2】Kubernetes 架构总览
  • 【android bluetooth 框架分析 02】【Module详解 12】【 BidiQueue、BidiQueueEnd、Queue介绍】
  • 10【借用·规则】引用 (``, `mut`):安全、高效地访问数据
  • 每日一题——最小测试用例集覆盖问题
  • 群内“分享”侵权书籍电子版,培训公司被判赔偿出版社2万元
  • 牛市早报|国常会:要持续稳定股市,4月LPR今日公布
  • 从黄仁勋到美国消费者,都在“突围”
  • 石黑一雄《莫失莫忘》与“克隆人”:殖民地的记忆与行动
  • 夜读丨“看看世界”本身就是一种意义
  • 财政部:一季度证券交易印花税411亿元,同比增长60.6%