Android多渠道打包(三):美团多渠道打包

来源:互联网 发布:网络热点事件2017 编辑:程序博客网 时间:2024/04/30 14:25

本章将介绍美团多渠道打包方法


Android多渠道打包(一):基础多渠道打包
Android多渠道打包(二):友盟多渠道打包
Android多渠道打包(三):美团多渠道打包
Android多渠道打包(四):360多渠道打包
Android多渠道打包(五):360多渠道打包+
Android多渠道打包(六):maven&gradle
Android多渠道打包(七):系列总结及展望


来源

本方法为美团点评技术团队2014.6.13日在《美团Android自动化之旅—生成渠道包》的文章中放出。

原理

将apk解包,然后在META-INF目录下创建一个以渠道号为文件名的空文件,再讲apk压缩,通过读取以渠道号为文件名的空文件的文件名来获取渠道号。
这里写图片描述

实现

  • 方法1: 使用python为apk文件添加渠道空文件
import zipfilezipped = zipfile.ZipFile(your_apk, 'a', zipfile.ZIP_DEFLATED) empty_channel_file = "META-INF/channel_{channel}".format(channel=your_channel)zipped.write(your_empty_file, empty_channel_file

这里写图片描述
* 方法2: 使用java代码export出jar文件进行脚本打包

java代码如下(引用自魏成林)

public class Tool {    private static final String CHANNEL_PREFIX = "/META-INF/";    private static final String CHANNEL_PATH_MATCHER = "regex:/META-INF/mtchannel_[0-9a-zA-Z]{1,5}";    private static String source_path;    private static final String channel_file_name = "channel_list.txt";    private static final String channel_flag = "channel_";    public static void main(String[] args) throws Exception {        if (args.length <= 0) {            System.out.println("请输入文件路径作为参数");            return;        }        final String source_apk_path = args[0];        int last_index = source_apk_path.lastIndexOf("/") + 1;        source_path = source_apk_path.substring(0, last_index);        final String source_apk_name = source_apk_path.substring(last_index, source_apk_path.length());        System.out.println("包路径:" + source_path);        System.out.println("文件名:" + source_apk_name);        ArrayList<String> channel_list = getChannelList(source_path + channel_file_name);        final String last_name = ".apk";        for (int i = 0; i < channel_list.size(); i++) {            final String new_apk_path = source_path + source_apk_name.substring(0, source_apk_name.length() - last_name.length()) //                    + "_" + channel_list.get(i) + last_name;            copyFile(source_apk_path, new_apk_path);            changeChannel(new_apk_path, channel_flag + channel_list.get(i));        }    }    /**     * 修改渠道号,原理是在apk的META-INF下新建一个文件名为渠道号的文件     */    public static boolean changeChannel(final String zipFilename, final String channel) {        try (FileSystem zipfs = createZipFileSystem(zipFilename, false)) {            final Path root = zipfs.getPath("/META-INF/");            ChannelFileVisitor visitor = new ChannelFileVisitor();            Files.walkFileTree(root, visitor);            Path existChannel = visitor.getChannelFile();            Path newChannel = zipfs.getPath(CHANNEL_PREFIX + channel);            if (existChannel != null) {                Files.move(existChannel, newChannel, StandardCopyOption.ATOMIC_MOVE);            } else {                Files.createFile(newChannel);            }            return true;        } catch (IOException e) {            System.out.println("添加渠道号失败:" + channel);            e.printStackTrace();        }        return false;    }    private static FileSystem createZipFileSystem(String zipFilename, boolean create) throws IOException {        final Path path = Paths.get(zipFilename);        final URI uri = URI.create("jar:file:" + path.toUri().getPath());        final Map<String, String> env = new HashMap<>();        if (create) {            env.put("create", "true");        }        return FileSystems.newFileSystem(uri, env);    }    private static class ChannelFileVisitor extends SimpleFileVisitor<Path> {        private Path channelFile;        private PathMatcher matcher = FileSystems.getDefault().getPathMatcher(CHANNEL_PATH_MATCHER);        public Path getChannelFile() {            return channelFile;        }        @Override        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {            if (matcher.matches(file)) {                channelFile = file;                return FileVisitResult.TERMINATE;            } else {                return FileVisitResult.CONTINUE;            }        }    }    /** 得到渠道列表 */    private static ArrayList<String> getChannelList(String filePath) {        ArrayList<String> channel_list = new ArrayList<String>();        try {            String encoding = "UTF-8";            File file = new File(filePath);            if (file.isFile() && file.exists()) { // 判断文件是否存在                InputStreamReader read = new InputStreamReader(new FileInputStream(file), encoding);// 考虑到编码格式                BufferedReader bufferedReader = new BufferedReader(read);                String lineTxt = null;                while ((lineTxt = bufferedReader.readLine()) != null) {                    // System.out.println(lineTxt);                    if (lineTxt != null && lineTxt.length() > 0) {                        channel_list.add(lineTxt);                    }                }                read.close();            } else {                System.out.println("找不到指定的文件");            }        } catch (Exception e) {            System.out.println("读取文件内容出错");            e.printStackTrace();        }        return channel_list;    }    /** 复制文件 */    private static void copyFile(final String source_file_path, final String target_file_path) throws IOException {        File sourceFile = new File(source_file_path);        File targetFile = new File(target_file_path);        BufferedInputStream inBuff = null;        BufferedOutputStream outBuff = null;        try {            // 新建文件输入流并对它进行缓冲            inBuff = new BufferedInputStream(new FileInputStream(sourceFile));            // 新建文件输出流并对它进行缓冲            outBuff = new BufferedOutputStream(new FileOutputStream(targetFile));            // 缓冲数组            byte[] b = new byte[1024 * 5];            int len;            while ((len = inBuff.read(b)) != -1) {                outBuff.write(b, 0, len);            }            // 刷新此缓冲的输出流            outBuff.flush();        } catch (Exception e) {            System.out.println("复制文件失败:" + target_file_path);            e.printStackTrace();        } finally {            // 关闭流            if (inBuff != null)                inBuff.close();            if (outBuff != null)                outBuff.close();        }    }}

将渠道号写入channel_list.text

channel_onechannel_twochannel_three

使用java命令脚本打包

java -jar tool.jar your.apk

  • 在android代码中获取渠道id
public static String getChannel(Context context) {        ApplicationInfo appinfo = context.getApplicationInfo();        String sourceDir = appinfo.sourceDir;        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("channel_")) {                    ret = entryName;                    break;                }            }        } catch (IOException e) {            e.printStackTrace();        } finally {            if (zipfile != null) {                try {                    zipfile.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }        String[] split = ret.split("_");        if (split != null && split.length >= 2) {            return ret.substring(split[0].length() + 1);        } else {            return "";        }    }

优缺点

比友盟高效点,只有解压缩和压缩,没有签名,兼容性也比较好,但是读取渠道号需要解压缩apk,速度比较慢

引用

美团Android自动化之旅—生成渠道包
魏成林:分享一种最简单的Android打渠道包的方法


1 0