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

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

首先我们说明一下什么是多渠道打包?

国内存在着众多的Android应用市场,为了统计不同安卓应用市场的下载量一个个性化统计需求,需要为每个应用市场的Android包设定一个可以区分应用市场的标识,这个为Android包设定应用市场标识的过程就是多渠道打包。

几种主流的多渠道打包方式,以及其优劣势

  • 通过配置gradle脚本实现多渠道打包

这种打包方式是使用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产品研发(五)--多渠道打包相关推荐

  1. [转]Android产品研发(十九)

    转载请标明出处:一片枫叶的专栏 上一篇文章中我们讲解了webview中问题集锦,讲解了webview的性能优化.webview种入Cookie信息.activity退出的时候清除webview信息报错 ...

  2. Android产品研发(十)--尽量不使用静态变量保存数据

    转载请标明出处:一片枫叶的专栏 上一篇文章中我们讲解了Android开发过程中几种常见网络协议:xml,json,protobuf等,以及它们各自的优缺点,一般而言当我们的App涉及到了网络传输时都会 ...

  3. Android产品研发(八)--App数据统计

    转载请标明出处:一片枫叶的专栏 上一篇文章中我们介绍了Android社区中比较火的热修复功能,并介绍了目前的几个比较流行的热修复框架,以及各自的优缺点,同时也介绍了一下自身项目中对热修复功能的实践.目 ...

  4. Android产品研发(十七)--Hybrid开发

    转载请标明出处:一片枫叶的专栏 上一篇文章中我们介绍了Android开发中经常会涉及到但又常常被忽视掉的开发者模式.主要讲解了包括如何打开手机的开发者模式,开发者模式中各个菜单的意义和作用,如何清除手 ...

  5. Android产品研发(十八)--webview问题集锦

    转载请标明出处:一片枫叶的专栏 上一篇文章中我们介绍了hybrid开发相关的知识.重点介绍了hybrid开发的概念,hybrid开发的作用,Android中如何实现hybrid开发,Android中实 ...

  6. Android产品研发(二十一)--Android中的UI优化

    转载请标明出处:一片枫叶的专栏 上一篇文章中我们讲解了Android产品研发过程中的代码Review.通过代码Review能够提高产品质量,增强团队成员之间的沟通,提高开发效率,所以良好的产品开发迭代 ...

  7. Android产品研发(二十)--代码Review

    转载请标明出处:一片枫叶的专栏 上一篇文章中我们讲解了如何在Android studio中进行单元测试.实际开发过程中有一些功能性的需求,比如测试工具类,测试数据存储等测试工作,如果还是通过重复执行a ...

  8. android gradle两种多渠道打包方式

    android gradle两种多渠道打包方式 gradle多渠道打包,目前已经了解到的有两种方式: 一种是利用gradle的manifestPlaceholders属性来替换渠道值 另一种是不同的渠 ...

  9. Android grade语法,多渠道打包

    Android grade语法,多渠道打包 文章目录 Android grade语法,多渠道打包 一.groovy语法 二.自动打包切换测试正式环境 三.gradle多渠道打包 总结 一.groovy ...

最新文章

  1. 点击后,过段时间cell自动取消选中
  2. 波卡链Substrate (7)Babe协议六“Secondary slot leader”
  3. 生产模式MTOMTS的区别
  4. 中小企业上云多地域办公组网,建立高质量云上内网环境
  5. http://longshuai2007.blog.163.com/blog/static/1420
  6. Teams 可被滥用于安装恶意软件,微软或不打算修复
  7. Go语言的基准测试简单示例
  8. 使用php第三方包mpdf将网页装换成pdf文件【2】
  9. 前端method常见请求方式
  10. IDEA TOP TIPS
  11. 怎么关闭vivo系统自检_MIUI11系统已发布,你怎么能不会这个一键关闭广告的功能呢!...
  12. SOA联姻IMS对3G无线网络是福是祸?
  13. 一文讲清楚什么是类型化数组、ArrayBuffer、TypedArray、DataView等概念
  14. python库声纹_什么是声纹数据库?
  15. Android前置后置摄像头录制视频综合版
  16. vivos9设置繁体语言方法分享
  17. Allegro中如何进行尺寸标注
  18. mse python_python3 MSE实现
  19. 为什么机油使用后变红_汽车机油用完后放出油红色怎么回事
  20. BGRABitmap图像操作9c:同时使用莫林杂点和 phong 阴影制作纹理

热门文章

  1. mysql制作排行榜_mysql实现排行榜
  2. H-ui.admin v3.1多图片预览上传的问题解决
  3. BCT2186 1A低压差线性稳压器
  4. 智慧树工业机器人测试答案_智慧树工业机器人答案章节单元测试答案
  5. UTL_HTTP read_text和read_raw应用
  6. 〖全域运营实战白宝书 - 运营角色认知篇②〗- 什么是运营?
  7. iOS 使用Zebra打印机打印标签
  8. 大数据读心术丨这15条数据统计准爆了!
  9. Excel·VBA一键计算每月合计
  10. 计算机16套常考真题,计算机二级16套精选真题020真题20