Android 快速渠道批量打包详解教程-美团多渠道打包方案

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

为什么要打渠道包

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

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

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和渠道标识
<!-- 友盟API Key -->  
<meta-data  
    android:name="UMENG_APPKEY"  
    android:value="***************">  
</meta-data>  

<!--umeng 渠道 -->  
 <meta-data   
     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.3 拷贝apk包到PythonTool目录下(与py同级)
apk包放置目录
  • 3.4 运行py脚本 MultiChannelBuildTool.py即可打包完成。

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


    打包完成

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

搬砖啦
搬砖啦

最新打包方案,还没有尝试过,有兴趣的可以看看(传送门)[https://github.com/mcxiaoke/packer-ng-plugin]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容