android 中三种多渠道打包方式

2024-06-19 13:18

本文主要是介绍android 中三种多渠道打包方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

http://gold.xitu.io/entry/578c98826be3ff006ce78317

http://gold.xitu.io/entry/578c98826be3ff006ce78317

android 中三种多渠道打包方式

转载请标明出处:一片枫叶的专栏

国内的Android开发者还是很苦逼的,由于众所周知的原因,google play无法再国内打开,所以android系的应用市场,群雄争霸,而后果就是国内存在着有众多的应用市场,产品在不同的渠道可能有这不同的统计需求,为此android开发人员需要为每个应用市场发布一个安装包,这里就涉及到了android的多渠道打包。

本文主要讲解的就是几种主流的多渠道打包方式,以及其优劣势。

这种打包方式是使用android Studio的编译工具gradle配合使用的,其核心原理就是通过脚本修改androidManifest.xml中的mate-date内容,执行N次打包签名操作实现多渠道打包的需求,具体实现如下。

(一)在androidmanifest.xml中定义mate-data标签

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="your.package.name">    <application>    <meta-data android:name="UMENG_CHANNEL" android:value="{UMENG}"/>    </application>    
</manifest>  

这里需要注意的是:上面的value的值要和渠道名所对应,比如wandoujia里面要对应为你豌豆荚的渠道名称

(二)在build.gradle下的productFlavors定义渠道号:

productFlavors {  internal {}  /*InHouse {}  pcguanwang {}  h5guanwang {}  hiapk {}  m91 {}  appchina {}  baidu {}  qq {}  jifeng {}  anzhi {}  mumayi {}  m360 {}  youyi {}  wandoujia {}  xiaomi {}  sougou {}  leshangdian {}  huawei {}  uc {}  oppo {}  flyme {}  jinli {}  letv {}*/  productFlavors.all { flavor ->  flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]  }  }    

同时需要注意的是,这里需要在defaultConfig中配置一个默认的渠道名称

manifestPlaceholders = [UMENG_CHANNEL_VALUE: "channel_name"]  

实现多渠道打包更换mate-data标签中的内容

优势:方便灵活,可以根据自身的需求配置不同的渠道执行不同的逻辑; 
劣势:打包速度过慢;

这种方式就是使用第三方的服务,比如360,百度,友盟等,其原理也是通过修改androidManifest.xml中的mate-data标签内容,然后执行N次打包签名的操作实现多渠道打包的。这里就不在做具体解释说明,免得又做广告的嫌疑,O(∩_∩)O哈哈~。

优势:简单方便,几乎不用自身做什么工作; 
劣势:打包速度过慢;

而这里主要是根据美团客户端打包经验(详见:美团Android自动化之旅—生成渠道包) 
主要是介绍利用在META-INF目录内添加空文件的方式,实现批量快速打包Android应用。

实现原理

Android应用安装包apk文件其实是一个压缩文件,可以将后缀修改为zip直接解压。解压安装文件后会发现在根目录有一个META-INF目录。如果在META-INF目录内添加空文件,可以不用重新签名应用。因此,通过为不同渠道的应用添加不同的空文件,可以唯一标识一个渠道。 
“采用这种方式,每打一个渠道包只需复制一个apk,在META-INF中添加一个使用渠道号命名的空文件即可。这种打包方式速度非常快,900多个渠道不到一分钟就能打完。”

实现步骤

(一)编写渠道号文件

(二)编写python脚本,实现解压缩apk文件,为META-INF目录添加文件,重新压缩apk文件等逻辑:


# coding=utf-8
import zipfile
import shutil
import osdef delete_file_folder(src):  '''delete files and folders''' if os.path.isfile(src):  try:  os.remove(src)  except:  pass elif os.path.isdir(src):  for item in os.listdir(src):  itemsrc=os.path.join(src,item)  delete_file_folder(itemsrc)  try:  os.rmdir(src)  except:  pass# 创建一个空文件,此文件作为apk包中的空文件
src_empty_file = 'info/empty.txt'
f = open(src_empty_file,'w')
f.close()# 在渠道号配置文件中,获取指定的渠道号
channelFile = open('./info/channel.txt','r')
channels = channelFile.readlines()
channelFile.close()
print('-'*20,'all channels','-'*20)
print(channels)
print('-'*50)# 获取当前目录下所有的apk文件
src_apks = [];
for file in os.listdir('.'):if os.path.isfile(file):extension = os.path.splitext(file)[1][1:]if extension in 'apk':src_apks.append(file)# 遍历所以的apk文件,向其压缩文件中添加渠道号文件
for src_apk in src_apks:src_apk_file_name = os.path.basename(src_apk)print('current apk name:',src_apk_file_name)temp_list = os.path.splitext(src_apk_file_name)src_apk_name = temp_list[0]src_apk_extension = temp_list[1]apk_names = src_apk_name.split('-');output_dir = 'outputDir'+'/'if os.path.exists(output_dir):delete_file_folder(output_dir)if not os.path.exists(output_dir):os.mkdir(output_dir)# 遍历从文件中获得的所以渠道号,将其写入APK包中for line in channels:target_channel = line.strip()target_apk = output_dir + apk_names[0] + "-" + target_channel+"-"+apk_names[2] + src_apk_extensionshutil.copy(src_apk,  target_apk)zipped = zipfile.ZipFile(target_apk, 'a', zipfile.ZIP_DEFLATED)empty_channel_file = "META-INF/uuchannel_{channel}".format(channel = target_channel)zipped.write(src_empty_file, empty_channel_file)zipped.close()print('-'*50)
print('repackaging is over ,total package: ',len(channels))
input('\npackage over...')

(三)打包一个正常的apk包 
(四)执行python脚本,多渠道打包 
(五)android代码中获取渠道号

/*** 渠道号工具类:解析压缩包,从中获取渠道号*/
public class ChannelUtil {private static final String CHANNEL_KEY = "uuchannel";private static final String DEFAULT_CHANNEL = "internal";private static String mChannel;public static String getChannel(Context context) {return getChannel(context, DEFAULT_CHANNEL);}public static String getChannel(Context context, String defaultChannel) {if (!TextUtils.isEmpty(mChannel)) {return mChannel;}//从apk中获取mChannel = getChannelFromApk(context, CHANNEL_KEY);if (!TextUtils.isEmpty(mChannel)) {return mChannel;}//全部获取失败return defaultChannel;}
   /*** 从apk中获取版本信息** @param context* @param channelKey* @return*/private static String getChannelFromApk(Context context, String channelKey) {long startTime = System.currentTimeMillis();//从apk包中获取ApplicationInfo appinfo = context.getApplicationInfo();String sourceDir = appinfo.sourceDir;//默认放在meta-inf/里, 所以需要再拼接一下String key = "META-INF/" + channelKey;String ret = "";ZipFile zipfile = null;try {zipfile = new ZipFile(sourceDir);Enumeration<?> entries = zipfile.entries();while (entries.hasMoreElements()) {ZipEntry entry = ((ZipEntry) entries.nextElement());String entryName = entry.getName();if (entryName.startsWith(key)) {ret = entryName;break;}}} catch (IOException e) {e.printStackTrace();} finally {if (zipfile != null) {try {zipfile.close();} catch (IOException e) {e.printStackTrace();}}}String channel = "";if (!TextUtils.isEmpty(ret)) {String[] split = ret.split("_");if (split != null && split.length >= 2) {channel = ret.substring(split[0].length() + 1);}System.out.println("-----------------------------");System.out.println("渠道号:" + channel + ",解压获取渠道号耗时:" + (System.currentTimeMillis() - startTime) + "ms");System.out.println("-----------------------------");} else {System.out.println("未解析到相应的渠道号,使用默认内部渠道号");channel = DEFAULT_CHANNEL;}return channel;}
}

整个打包的流程就是这样了,打工工具可参考github上的项目:多渠道打包实现

优势:打包速度很快,很方便; 
劣势:不够灵活,不能灵活的配置不同的渠道不同的业务逻辑;

问题: 
项目中由于使用了友盟统计,以前是在meta-data中保存渠道信息,现在更改了方式之后需要手动执行渠道号的设置代码:

String channel = ChannelUtil.getChannel(mContext)System.out.println("启动页获取到的渠道号为:" + channel)// 设置友盟统计的渠道号,原来是在Manifest文件中设置的meta-data,现在启动页中设置AnalyticsConfig.setChannel(channel)

通过这种打包方式以前需要一个小时的打包工作现在只需要一分钟即可,极大的提高了效率,目前在实际的应用中尚未发现有什么问题,有这种需求的童鞋可以尝试一下。

虽说我们总结了三种打包方式,但是其实通过gradle打包和使用第三方服务打包都是执行了N次的打包签名操作,时间上耗费太多,因此不太推荐,而美团的方式在效率上提高了很多,但是对于那种不同的渠道包执行不同的业务逻辑的需求就无能为例了,只能通过gradle配置,因此大家在选择多渠道打包方式的时候可以根据自身的需求来选择。

另外对产品研发技术,技巧,实践方面感兴趣的同学可以参考我的: 
android产品研发–>总结(持续更新) 
android产品研发(一)–>实用开发规范 
android产品研发(二)–>启动页优化 
android产品研发(三)–>基类Activity 
android产品研发(四)–>减小Apk大小

本文以同步至github中:https://github.com/yipianfengye/androidProject,欢迎star和follow


这篇关于android 中三种多渠道打包方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1075136

相关文章

SpringBoot中@Value注入静态变量方式

《SpringBoot中@Value注入静态变量方式》SpringBoot中静态变量无法直接用@Value注入,需通过setter方法,@Value(${})从属性文件获取值,@Value(#{})用... 目录项目场景解决方案注解说明1、@Value("${}")使用示例2、@Value("#{}"php

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

Vite 打包目录结构自定义配置小结

《Vite打包目录结构自定义配置小结》在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义... 目录一、实现原理二、具体配置步骤1. 基础配置文件2. 配置说明(1)js 资源分离(2)非 JS 资

JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法

《JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法》:本文主要介绍JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法,每种方法结合实例代码给大家介绍的非常... 目录引言:为什么"相等"判断如此重要?方法1:使用some()+includes()(适合小数组)方法2

HTTP 与 SpringBoot 参数提交与接收协议方式

《HTTP与SpringBoot参数提交与接收协议方式》HTTP参数提交方式包括URL查询、表单、JSON/XML、路径变量、头部、Cookie、GraphQL、WebSocket和SSE,依据... 目录HTTP 协议支持多种参数提交方式,主要取决于请求方法(Method)和内容类型(Content-Ty

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

使用shardingsphere实现mysql数据库分片方式

《使用shardingsphere实现mysql数据库分片方式》本文介绍如何使用ShardingSphere-JDBC在SpringBoot中实现MySQL水平分库,涵盖分片策略、路由算法及零侵入配置... 目录一、ShardingSphere 简介1.1 对比1.2 核心概念1.3 Sharding-Sp

Spring创建Bean的八种主要方式详解

《Spring创建Bean的八种主要方式详解》Spring(尤其是SpringBoot)提供了多种方式来让容器创建和管理Bean,@Component、@Configuration+@Bean、@En... 目录引言一、Spring 创建 Bean 的 8 种主要方式1. @Component 及其衍生注解

python中的显式声明类型参数使用方式

《python中的显式声明类型参数使用方式》文章探讨了Python3.10+版本中类型注解的使用,指出FastAPI官方示例强调显式声明参数类型,通过|操作符替代Union/Optional,可提升代... 目录背景python函数显式声明的类型汇总基本类型集合类型Optional and Union(py

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优