Skip to content

Instantly share code, notes, and snippets.

@panpf
Created June 24, 2016 06:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save panpf/b176cf1037b26fe1857cbef3adbf3ead to your computer and use it in GitHub Desktop.
Save panpf/b176cf1037b26fe1857cbef3adbf3ead to your computer and use it in GitHub Desktop.
一种通过往META-INF目录下添加特殊名称的文件实现多渠道APK的方法
import java.io.*;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
public class ChannelApkGenerator {
private static final String CHANNEL_PREFIX = "/META-INF/";
private static final String CHANNEL_FILE_NAME = "channels.txt";
private static final String FILE_NAME_CONNECTOR = "-";
private static final String CHANNEL_FLAG = "channel_";
public static void main(String[] args) {
if (args.length <= 0) {
System.out.println("请输入文件路径");
return;
}
final String apkFilePath = args[0];
File apkFile = new File(apkFilePath);
if (!apkFile.exists()) {
System.out.println("找不到文件:" + apkFile.getPath());
return;
}
String existChannel;
try {
existChannel = readChannel(apkFile);
} catch (IOException e) {
e.printStackTrace();
return;
}
if(existChannel != null){
System.out.println("此安装包已存在渠道号:" + existChannel + ",请使用原始包");
return;
}
String parentDirPath = apkFile.getParent();
if(parentDirPath == null){
System.out.println("请输入完整的文件路径:" + apkFile.getPath());
return;
}
String fileName = apkFile.getName();
int lastPintIndex = fileName.lastIndexOf(".");
String fileNamePrefix;
String fileNameSurfix;
if (lastPintIndex != -1) {
fileNamePrefix = fileName.substring(0, lastPintIndex);
fileNameSurfix = fileName.substring(lastPintIndex, fileName.length());
} else {
fileNamePrefix = fileName;
fileNameSurfix = "";
}
LinkedList<String> channelList = getChannelList(new File(apkFile.getParentFile(), CHANNEL_FILE_NAME));
if(channelList == null){
return;
}
for (String channel : channelList) {
String newApkPath = parentDirPath + File.separator + fileNamePrefix + FILE_NAME_CONNECTOR + channel + fileNameSurfix;
try {
copyFile(apkFilePath, newApkPath);
} catch (IOException e) {
e.printStackTrace();
break;
}
if(!changeChannel(newApkPath, CHANNEL_FLAG + channel)){
break;
}
}
}
/**
* 读取渠道列表
*/
private static LinkedList<String> getChannelList(File channelListFile) {
if (!channelListFile.exists()) {
System.out.println("找不到渠道配置文件:" + channelListFile.getPath());
return null;
}
if (!channelListFile.isFile()) {
System.out.println("这不是一个文件:" + channelListFile.getPath());
return null;
}
LinkedList<String> channelList = null;
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(channelListFile), "UTF-8"));
String lineTxt;
while ((lineTxt = reader.readLine()) != null) {
lineTxt = lineTxt.trim();
if (lineTxt.length() > 0) {
if (channelList == null) {
channelList = new LinkedList<>();
}
channelList.add(lineTxt);
}
}
} catch (IOException e) {
System.out.println("读取渠道配置文件失败:" + channelListFile.getPath());
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return channelList;
}
/**
* 复制文件
*/
private static void copyFile(final String sourceFilePath, final String targetFilePath) throws IOException {
File sourceFile = new File(sourceFilePath);
File targetFile = new File(targetFilePath);
if(targetFile.exists()){
//noinspection ResultOfMethodCallIgnored
targetFile.delete();
}
BufferedInputStream inputStream = null;
BufferedOutputStream outputStream = null;
try {
inputStream = new BufferedInputStream(new FileInputStream(sourceFile));
outputStream = new BufferedOutputStream(new FileOutputStream(targetFile));
byte[] b = new byte[1024 * 8];
int realReadLength;
while ((realReadLength = inputStream.read(b)) != -1) {
outputStream.write(b, 0, realReadLength);
}
} catch (Exception e) {
System.out.println("复制文件失败:" + targetFilePath);
e.printStackTrace();
} finally {
if (inputStream != null){
inputStream.close();
}
if (outputStream != null){
outputStream.flush();
outputStream.close();
}
}
}
/**
* 添加渠道号,原理是在apk的META-INF下新建一个文件名为渠道号的文件
*/
private static boolean changeChannel(final String newApkFilePath, final String channel) {
try (FileSystem fileSystem = createZipFileSystem(newApkFilePath, false)){
final Path root = fileSystem.getPath(CHANNEL_PREFIX);
ChannelFileVisitor visitor = new ChannelFileVisitor();
try {
Files.walkFileTree(root, visitor);
} catch (IOException e) {
e.printStackTrace();
System.out.println("添加渠道号失败:" + channel);
e.printStackTrace();
return false;
}
Path existChannel = visitor.getChannelFile();
if (existChannel != null) {
System.out.println("此安装包已存在渠道号:" + existChannel.getFileName().toString() + ", FilePath: " + newApkFilePath);
return false;
}
Path newChannel = fileSystem.getPath(CHANNEL_PREFIX + channel);
try {
Files.createFile(newChannel);
} catch (IOException e) {
System.out.println("添加渠道号失败:" + channel);
e.printStackTrace();
return false;
}
System.out.println("添加渠道号成功:" + channel+", NewFilePath:" + newApkFilePath);
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;
public Path getChannelFile() {
return channelFile;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.getFileName().toString().startsWith(CHANNEL_FLAG)) {
channelFile = file;
return FileVisitResult.TERMINATE;
} else {
return FileVisitResult.CONTINUE;
}
}
}
private static String readChannel(File apkFile) throws IOException {
FileSystem zipFileSystem;
try {
zipFileSystem = createZipFileSystem(apkFile.getPath(), false);
} catch (IOException e) {
e.printStackTrace();
System.out.println("读取渠道号失败:" + apkFile.getPath());
throw e;
}
final Path root = zipFileSystem.getPath(CHANNEL_PREFIX);
ChannelFileVisitor visitor = new ChannelFileVisitor();
try {
Files.walkFileTree(root, visitor);
} catch (IOException e) {
e.printStackTrace();
System.out.println("读取渠道号失败:" + apkFile.getPath());
throw e;
}
Path existChannel = visitor.getChannelFile();
return existChannel != null ? existChannel.getFileName().toString() : null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment