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

相关文章

Jsoncpp的安装与使用方式

《Jsoncpp的安装与使用方式》JsonCpp是一个用于解析和生成JSON数据的C++库,它支持解析JSON文件或字符串到C++对象,以及将C++对象序列化回JSON格式,安装JsonCpp可以通过... 目录安装jsoncppJsoncpp的使用Value类构造函数检测保存的数据类型提取数据对json数

Redis事务与数据持久化方式

《Redis事务与数据持久化方式》该文档主要介绍了Redis事务和持久化机制,事务通过将多个命令打包执行,而持久化则通过快照(RDB)和追加式文件(AOF)两种方式将内存数据保存到磁盘,以防止数据丢失... 目录一、Redis 事务1.1 事务本质1.2 数据库事务与redis事务1.2.1 数据库事务1.

Linux磁盘分区、格式化和挂载方式

《Linux磁盘分区、格式化和挂载方式》本文详细介绍了Linux系统中磁盘分区、格式化和挂载的基本操作步骤和命令,包括MBR和GPT分区表的区别、fdisk和gdisk命令的使用、常见的文件系统格式以... 目录一、磁盘分区表分类二、fdisk命令创建分区1、交互式的命令2、分区主分区3、创建扩展分区,然后

Linux中chmod权限设置方式

《Linux中chmod权限设置方式》本文介绍了Linux系统中文件和目录权限的设置方法,包括chmod、chown和chgrp命令的使用,以及权限模式和符号模式的详细说明,通过这些命令,用户可以灵活... 目录设置基本权限命令:chmod1、权限介绍2、chmod命令常见用法和示例3、文件权限详解4、ch

Java中的密码加密方式

《Java中的密码加密方式》文章介绍了Java中使用MD5算法对密码进行加密的方法,以及如何通过加盐和多重加密来提高密码的安全性,MD5是一种不可逆的哈希算法,适合用于存储密码,因为其输出的摘要长度固... 目录Java的密码加密方式密码加密一般的应用方式是总结Java的密码加密方式密码加密【这里采用的

Java中ArrayList的8种浅拷贝方式示例代码

《Java中ArrayList的8种浅拷贝方式示例代码》:本文主要介绍Java中ArrayList的8种浅拷贝方式的相关资料,讲解了Java中ArrayList的浅拷贝概念,并详细分享了八种实现浅... 目录引言什么是浅拷贝?ArrayList 浅拷贝的重要性方法一:使用构造函数方法二:使用 addAll(

Python pyinstaller实现图形化打包工具

《Pythonpyinstaller实现图形化打包工具》:本文主要介绍一个使用PythonPYQT5制作的关于pyinstaller打包工具,代替传统的cmd黑窗口模式打包页面,实现更快捷方便的... 目录1.简介2.运行效果3.相关源码1.简介一个使用python PYQT5制作的关于pyinstall

Mycat搭建分库分表方式

《Mycat搭建分库分表方式》文章介绍了如何使用分库分表架构来解决单表数据量过大带来的性能和存储容量限制的问题,通过在一对主从复制节点上配置数据源,并使用分片算法将数据分配到不同的数据库表中,可以有效... 目录分库分表解决的问题分库分表架构添加数据验证结果 总结分库分表解决的问题单表数据量过大带来的性能

javafx 如何将项目打包为 Windows 的可执行文件exe

《javafx如何将项目打包为Windows的可执行文件exe》文章介绍了三种将JavaFX项目打包为.exe文件的方法:方法1使用jpackage(适用于JDK14及以上版本),方法2使用La... 目录方法 1:使用 jpackage(适用于 JDK 14 及更高版本)方法 2:使用 Launch4j(

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO