android打包工具多渠道批量打包,Android 快速渠道批量打包详解教程-美团多渠道打包方案...

今天写一篇文章来总结下android批量打渠道包美团版本。之前项目上一直用的是gradle 批量打包方式,那个速度啊真是令人发指,15个渠道得跑上半个小时,出去吃顿饭回来,还在跑。特别是赶上项目上线的话,如果给测试提交了正式版本1.0,突然发现有小bug要修复,修复后又得重新批量打包半个小时。。。。无语啦。。。真佩服以前的耐心。。。。好了今天来看下打包方案 - 美团多渠道打包方案。至于为撒是美团,应该是这个方案是美团的哪一位大神放出来的吧。

为什么要打渠道包

为什么要渠道打包,一个包不是挺好的吗,一个包也可以发布到各个应用市场嘛?以前刚入门时候也是傻乎乎的这么想的。如果现在你老板提出这样需求场景:亮仔呀,我想知道我们的APP在哪个应用市场渠道下载的最多,我们以后就重点推广这个渠道,用钱砸到排名前面!!! 亮仔傻眼了!!—— 所以不同渠道打包主要用来做统计分析,特别是游戏应用,特别注意哪个渠道推广的最有效。

下面是友盟平台,APP统计各个渠道的分析图:

5d1ad9839c90

TIM图片20170620101652.png

从统计图可以看出APP在各个渠道的用户、活跃度、启动次数、活动时长....还是比较详细的,对运营团队有着很大的指导作用哈。

怎么打渠道包

怎么渠道打包呢?亮仔就开始想了:这还不简单,三步搞定:

1.我在Adminifest.xml文件里面配一个meta-data值,这个值写死成某个渠道;

2.在用户安装了我们的APP后,我获取这个写死的渠道值然后上传到后台;

3.我挨个修改meta-data值,改成各个渠道然后编译打包10分钟搞定;

很快亮仔就开始上手,很快实现了老板提出的需求!!! 好景不长... 老板:亮仔啊,我们这个应用下载量不给力啊,推广不够啊,这样我们把我们APP发到市场上所有渠道上,广撒网捕鱼嘛,也不多就100多个渠道吧!!! 亮仔慌了:我擦,我打一个包需要2分钟,100个包 3个多小时啊 这一天光打包了.....

所以基于上面的场景,我们发现主要有两个问题:

打包的本质是将渠道标识传递给后台

这一步已经有第三方平台帮我们做了,实现的思路应该也也差不多,我们集成友盟的渠道统计分析即可。没有必要写一套自己的渠道统计分析。友盟渠道统计接入传送门>>

怎样快速打包

为什么打包会花那么长时间?因为每个渠道打一次包,就要重新编译一次,所以耗时长。其实只要想办法将打好的一个包,替换里面的meta-data值即可。我们来看下美团多渠道打包是怎么做的:

1.首先你需要安装python环境

不要被python环境搭建吓到,其实就跟安装一个普通的exe软件差不多,下一步。。安装后不需要配置什么环境变量之类。 [python下载传送门 >>](https://www.python.org/downloads/)

2.项目中接入友盟统计

2.1 申请友盟的账号

在Adminifest.xml中配置友盟ID和渠道标识

android:name="UMENG_APPKEY"

android:value="***************">

android:name="UMENG_CHANNEL"

android:value="baidu" />

2.2 应用启动时上传渠道标识给友盟

ChannelUtil 是封装的一个获取渠道标识的工具类

public class MyApplication extends Application {

@Override

public void onCreate() {

super.onCreate();

String channel=ChannelUtil.getChannel(this, "default channel");//获取渠道名

AnalyticsConfig.setChannel(channel);//调用umeng api设置umeng渠道

}

}

ChaneUtil工具类直接偷来的~~

import java.io.IOException;

import java.util.Enumeration;

import java.util.zip.ZipEntry;

import java.util.zip.ZipFile;

import android.content.Context;

import android.content.SharedPreferences;

import android.content.SharedPreferences.Editor;

import android.content.pm.ApplicationInfo;

import android.content.pm.PackageManager.NameNotFoundException;

import android.preference.PreferenceManager;

import android.text.TextUtils;

public class ChannelUtil {

private static final String CHANNEL_KEY = "cztchannel";

private static final String CHANNEL_VERSION_KEY = "cztchannel_version";

private static String mChannel;

/**

* 返回市场。 如果获取失败返回""

* @param context

* @return

*/

public static String getChannel(Context context){

return getChannel(context, "");

}

/**

* 返回市场。 如果获取失败返回defaultChannel

* @param context

* @param defaultChannel

* @return

*/

public static String getChannel(Context context, String defaultChannel) {

//内存中获取

if(!TextUtils.isEmpty(mChannel)){

return mChannel;

}

//sp中获取

mChannel = getChannelBySharedPreferences(context);

if(!TextUtils.isEmpty(mChannel)){

return mChannel;

}

//从apk中获取

mChannel = getChannelFromApk(context, CHANNEL_KEY);

if(!TextUtils.isEmpty(mChannel)){

//保存sp中备用

saveChannelBySharedPreferences(context, mChannel);

return mChannel;

}

//全部获取失败

return defaultChannel;

}

/**

* 从apk中获取版本信息

* @param context

* @param channelKey

* @return

*/

private static String getChannelFromApk(Context context, String channelKey) {

//从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[] split = ret.split("_");

String channel = "";

if (split != null && split.length >= 2) {

channel = ret.substring(split[0].length() + 1);

}

return channel;

}

/**

* 本地保存channel & 对应版本号

* @param context

* @param channel

*/

private static void saveChannelBySharedPreferences(Context context, String channel){

SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);

Editor editor = sp.edit();

editor.putString(CHANNEL_KEY, channel);

editor.putInt(CHANNEL_VERSION_KEY, getVersionCode(context));

editor.commit();

}

/**

* 从sp中获取channel

* @param context

* @return 为空表示获取异常、sp中的值已经失效、sp中没有此值

*/

private static String getChannelBySharedPreferences(Context context){

SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);

int currentVersionCode = getVersionCode(context);

if(currentVersionCode == -1){

//获取错误

return "";

}

int versionCodeSaved = sp.getInt(CHANNEL_VERSION_KEY, -1);

if(versionCodeSaved == -1){

//本地没有存储的channel对应的版本号

//第一次使用 或者 原先存储版本号异常

return "";

}

if(currentVersionCode != versionCodeSaved){

return "";

}

return sp.getString(CHANNEL_KEY, "");

}

/**

* 从包信息中获取版本号

* @param context

* @return

*/

private static int getVersionCode(Context context){

try{

return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;

}catch(NameNotFoundException e) {

e.printStackTrace();

}

return -1;

}

}

3.运行打包脚本打包

3.1 下载打包脚本

5d1ad9839c90

下载解压

3.2 配置渠道

5d1ad9839c90

配置渠道

3.3 拷贝apk包到PythonTool目录下(与py同级)

5d1ad9839c90

apk包放置目录

3.4 运行py脚本 MultiChannelBuildTool.py即可打包完成。

(生成的渠道apk包在output_** 目录下)

5d1ad9839c90

打包完成

ok,写完啦!刚开始练习写总结文章,有讲的不清楚的,欢迎指正!!

4423

搬砖啦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/435994.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【转】云社区 博客 博客详情 二维异形件排版算法介绍(一)

转自:https://bbs.huaweicloud.com/blogs/175385 【摘要】 排样问题(Nesting Problem)又称为下料问题(Cutting and stock problems)或填充问题(Packing Problem),其目标是在材料切割过程中寻找一个较高的材料利用率。排样问题属于…

增加RIL组件时编辑出现的问题

当我把RIL组件加进到CE6的项目里去编译,本来好好的项目编译不过去了,我就选择了RIL的所有feature,没有改任何东西,理论上sysgen就可以了,但是确总是编译不过去 看根目录下的build.log: E:/WINCE600/OSDesigns/POS410/POS410/Wince600/E6000_ARMV4I/cesy…

android 微信两个服务的,微信上线两个新功能

微信又上线新功能了!一个与你的钱包相关,一个与你的微信号相关,赶紧来看看吧微信支付分全面开放每个微信用户都拥有自己的一个分数值 ,且每月根据综合数据更新一次。达到一定的分值门槛,用户即可享受超千项便捷服务。这…

【转】二维异形件排版算法介绍(二)

转自:https://bbs.huaweicloud.com/blogs/196289【摘要】 二维不规则异形件主要有两种策略:分别是基于可行解的排样策略和基于重叠移除的排样策略。所谓基于可行解的排样策略,是指在排样过程中零件之间始终是不重叠的,而基于重叠移…

解决用户控件循环引用的笨办法

在上一篇中提到使用MagicAjax开发了一个项目,因为做这个项目之前对MagicAjax不是很了解,在里面使用了用户控件,我发现这是一个很致命的错误,用户控件在MagicAjax中用起来很痛苦.因我们的项目不是简单的只要求"看起来像"没有刷新页面,其中一个主要的页面有那么一点点复…

【转】介绍一些免费/开源的医学影像后处理工具

转自:https://blog.csdn.net/liaopiankun0618/article/details/84328331 来源:融视影像科技 综述 医学影像的处理有两个特质。一是复杂,整个处理流程涉及多种算法,需要调整的参数较多。二是发展快,从采集、重建到后…

android 安卓市场,安卓市场(Android Market).doc

安卓市场(Android Market)安卓市场(Android Market)Android (HiMarket) China market is the earliest and largest Android software and online game download platform, Netdragon well-known products, provide good service for users of mobile phone software. Since it…

IROM 浅见

IROM其实是三星已经固化在CPU里面的一段代码,我们要做的事情就是提供一段可以供IROM读取的8K代码程序,至于这8K代码放在哪里,由CPU的相关引脚决定(OM1-OM4,GPN13-GPN15)。也就是说从哪里启动的意思。 如果从…

LINQ是死是活?——很奇怪为什么会有这样的话题?

LINQ是死是活??? 如果我问您:机器代码死了吗?汇编死了吗?C死了吗?VB6.0死了吗?Delphi死了吗?您可能会觉得我很无聊。所以我觉得“LINQ是死是活”的说法也很奇怪? LINQ诞…

android gradle 语法,Gradle 1.语法

8种机械键盘轴体对比本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?gradle 语法解析:gradle的语法十分简洁,以至于看起来跟像是配置文件。先看一个简单的android项目完整的gradle脚本:build.gradle123456789101112131415…

【转】二维异形件排版算法介绍(三)

转自:https://bbs.huaweicloud.com/blogs/203947 【摘要】 相比于基于可行解的排样算法,重叠移除算法在改变解的状态时,允许零件之间发生重叠,然后采用分离技术消除重叠,直到达到算法的终止条件为止。重叠移除算法的关…

WinCE6.0中应用程序如何直接访问物理空间

在实际开发过程中,经常希望能在应用程序中直接读写设备的物理空间。以前在做WinCE6.0下的MEMMgr时通过秘密加载一个内核态驱动实现了这个需求。但这种方式有一个明显的缺陷,每次读写都必须经由它才能完成。如果只是读取GPIO,那问题不算大。如…

开博之文

逛园子很久了,读而不写非礼也。分享技术,交流经验,体味人生,本博主旨,此为09年的开年大事。转载于:https://www.cnblogs.com/imstand/archive/2009/01/11/1373712.html

【转】Dicom格式文件解析器!!!!!!!

转自:https://www.cnblogs.com/assassinx/archive/2013/01/09/dicomViewer.html Dicom全称是医学数字图像与通讯,这里讲的暂不涉及通讯的问题 只讲*.dcm 也就是diocm格式文件的读取,读取本身是没啥难度的 无非就是字节码数据流处理。只不过确…

android 设置view最大高度,android-在RecyclerView上设置最大高度

我有一个对话框片段,其中包含线性布局,其中涉及RecyclerView上方的titleText,在最底部,recyclerView下方有一个按钮.由于recyclerView根据适配器设置的项目数展开或折叠,因此按钮有时会被截断并且不再显示在屏幕上,因为recyclerView仅覆盖整个屏幕.我的问题是,有没有一种方法可…

【转贴】利用 Javascript 获取 URL 参数(适合IE、FF)

.  ... .../test.html?str123456如何用Javascript接收类似上面的url参数呢?这个问题网上有很多答案,看了看,主要有两种,一个是利用字符串的截取,另一种是利用正则表达式。[正则式&#xff3d…

【WinCE】流设备驱动简介及GPIO驱动的实现

流设备驱动实际上就是导出标准的流接口函数的驱动,这是文档上面的定义。在WinCE中,所有的流设备都导出流设备接口,这样WinCE中的Device Manager可以加载和管理这些流设备驱动。 流设备驱动的架构如图: 首先我声明一下,…

android contentprovider api,Content Provider Basics

一个内容提供者访问数据的中央资源库。提供者是应用程序的一部分,提供自己的操作数据的UI。然而,内容提供者主要是被其他应用程序引用,通过提供者客户对象访问提供者。提供者和提供者客户端为数据提供一个一致的,标准的接口&#…

【转】医学影像调窗技术!!!!

转自:https://www.cnblogs.com/assassinx/p/3139505.html 在年初的时候做过一个dicom格式文件解析,当时只是提了下。看着跟别人的显示出来也差不多 其实是我想太简单了。整理了下思路 这里提供正确的调窗代码。 医学影像 说得挺高科技的 其实在这个过程…

开始-运行 下常用快捷命令

在“开始”菜单上单击“运行“,输入 1. eventvwr.msc /s 打开事件查看器 (可查看系统日志) 2.gpedit.msc 打开组策略 示例:在Windows Server 2003中关闭系统事件跟踪程序: 单击“开始→运行”,输入gped…