zoukankan      html  css  js  c++  java
  • 美团多渠道打包

    一.原理

    把一个Android应用包当作zip文件包进行解压,然后发现在签名生成的目录下(META-INF)添加一个空文件不需要重新签名。利用这个机制,该文件的文件名就是渠道名。这种方式不需要重新签名等步骤,非常高效。

    二.方法

    已经将美团的打包工具放到了tools下的test01文件中:
    1、将要打包的apk放到PythonTool中
    2、在PythonTool/info/channel.txt中写入需要的渠道,一个渠道占一行
    3、双击执行PythonTool/MultiChannelBuildTool.py文件(需要Python环境),就会生成渠道包
    4、获取渠道信息:将JavaUtil文件中的ChannelUtil.java拷贝到工程,调用ChannelUtil.getChannel即可获取渠道

    package com.czt.util;
    
    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;
    	}
    }
    

      

     工具下载地址:http://pan.baidu.com/share/link?shareid=1485267923&uk=4218015263#list/path=%2F

    三.优缺点

    优点:
    这种打包方式速度非常快,900多个渠道不到一分钟就能打完

    缺点:
    1、google如果哪天更改打包规则,使得在META-INF中建立空文件还需要重新打包,这种方式将不可用

    2、一些不法的渠道商很容易通过工具修改渠道,如果一个渠道商,通过网络劫持和篡改渠道的组合方式来获取暴利,对于程序开发者来说可能会存在着巨大的经济损失
     

  • 相关阅读:
    【新特性速递】数字输入框的前缀和后缀(位于输入框内部)
    【新特性速递】进度条,进度条,进度条
    【新特性速递】当法语遇上FineUI(Bonjour)!
    【新特性速递】自定义数字输入框的小数分隔符和千分位分隔符
    【经验分享】FineUICore中如何处理文件导出异常?
    【网友作品】服装分销系统架构与界面分享(基于FineUICore基础版)
    FineUIPro/Mvc/Core v6.3.0 正式发布了!
    星球居民突破 1700 人!
    【新特性速递】开关样式复选框增强!
    【新特性速递】为RenderField新增QuickSortField属性!
  • 原文地址:https://www.cnblogs.com/ganchuanpu/p/6505063.html
Copyright © 2011-2022 走看看