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


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


来源

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

原理

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

实现

  • 方法1: 使用python为apk文件添加渠道空文件
import zipfile
zipped = 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_one
channel_two
channel_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. android 文件读取(assets)
  2. JAVA环境下创建JSP文件报错: The superclass "javax.servlet.http
  3. 【JavaWeb-6】HttpServletResponse的字符字节输出流、编码、文件
  4. Java 简单解决springmvc获取properties文件里面中文内容出现论码
  5. 解决Eclipse建立Maven项目后无法建立src/main/java资源文件夹的
  6. java读写中文文件
  7. java文件上传输入输出流的问题
  8. Android MediaCodec硬解码AAC音频文件(实时AAC音频帧)并播放
  9. 获取所有音乐文件的专辑封面

随机推荐

  1. 11、ffmpeg学习笔记—ffmpeg源码编译-And
  2. PullToRefreshListView addHeadView的正
  3. javaAndroid实现刚刚发表几天前的日期工
  4. Android SDK可以与JDK 1.7兼容吗?
  5. android开发中调用系统中分享功能的方法
  6. 我怎样才能获得移动方向
  7. Android核心分析 之十-------Android GWE
  8. 在string.xml中调用变量[重复]
  9. Android listview+SwipeRefreshLayout 下
  10. android webview对shouldOverrideUrlLoad