转载自https://blog.csdn.net/yypblog/article/details/40481901
略经修改。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Enumeration;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class ApkDemo02 {
private static final int BUFFER = 1024;
private static final String BASE_DIR = "";
/** 符号"/"用来作为目录标识判断符 */
private static final String PATH = "/";
/** 签名目录 */
private static final String SIGN_PATH_NAME = "META-INF";
/** 解压源文件目录 */
private static final String SOURCE_PATH_NAME = "\\source\\";
/** 打包目录 */
private static final String TARGET_PATH_NAME = "\\target\\";
/** 签名目录 */
private static final String RESULT_PATH_NAME = "\\result\\";
/** JDK BIN 目录 此处依配置情况酌情修改*/
//此处依配置情况酌情修改
private static final String JDK_BIN_PATH = "C:\\Program Files\\Java\\jdk1.7.0_21\\bin";
/** 密钥 目录 */
//此处依个人情况酌情修改
private static final String SECRET_KEY_PATH = "C:\\Users\\Jakieenchan\\Desktop\\Analyse_Apk\\keystore\\";
/** 密钥 名称 */
//此处依个人情况酌情修改
private static final String SECRET_KEY_NAME = "abc.keystore";
/**
* 解压缩zip文件
*
* @param fileName
* 要解压的文件名 包含路径 如:"c:\\test.zip"
* @param filePath
* 解压后存放文件的路径 如:"c:\\temp"
* @throws Exception
*/
@SuppressWarnings("rawtypes")
public static void unZip(String fileName, String filePath) throws Exception {
ZipFile zipFile = new ZipFile(fileName);
Enumeration emu = zipFile.entries();
while (emu.hasMoreElements()) {
ZipEntry entry = (ZipEntry) emu.nextElement();
if (entry.isDirectory()) {
new File(filePath + entry.getName()).mkdirs();
continue;
}
BufferedInputStream bis = new BufferedInputStream(
zipFile.getInputStream(entry));
File file = new File(filePath + entry.getName());
File parent = file.getParentFile();
if (parent != null && (!parent.exists())) {
parent.mkdirs();
}
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER);
byte[] buf = new byte[BUFFER];
int len = 0;
while ((len = bis.read(buf, 0, BUFFER)) != -1) {
fos.write(buf, 0, len);
}
bos.flush();
bos.close();
bis.close();
}
zipFile.close();
}
/**
* 压缩文件
*
* @param srcFile
* @param destPath
* @throws Exception
*/
public static void compress(String srcFile, String destPath)
throws Exception {
compress(new File(srcFile), new File(destPath));
}
/**
* 压缩
*
* @param srcFile
* 源路径
* @param destPath
* 目标路径
* @throws Exception
*/
public static void compress(File srcFile, File destFile) throws Exception {
// 对输出文件做CRC32校验
CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(
destFile), new CRC32());
ZipOutputStream zos = new ZipOutputStream(cos);
compress(srcFile, zos, BASE_DIR);
zos.flush();
zos.close();
}
/**
* 压缩
*
* @param srcFile
* 源路径
* @param zos
* ZipOutputStream
* @param basePath
* 压缩包内相对路径
* @throws Exception
*/
private static void compress(File srcFile, ZipOutputStream zos,
String basePath) throws Exception {
if (srcFile.isDirectory()) {
compressDir(srcFile, zos, basePath);
} else {
compressFile(srcFile, zos, basePath);
}
}
/**
* 压缩目录
*
* @param dir
* @param zos
* @param basePath
* @throws Exception
*/
private static void compressDir(File dir, ZipOutputStream zos,
String basePath) throws Exception {
File[] files = dir.listFiles();
// 构建空目录
if (files.length < 1) {
ZipEntry entry = new ZipEntry(basePath + dir.getName() + PATH);
zos.putNextEntry(entry);
zos.closeEntry();
}
String dirName = "";
String path = "";
for (File file : files) {
// 当父文件包名为空时,则不把包名添加至路径中(主要是解决压缩时会把父目录文件也打包进去)
if (basePath != null && !"".equals(basePath)) {
dirName = dir.getName();
}
path = basePath + dirName + PATH;
// 递归压缩
compress(file, zos, path);
}
}
/**
* 文件压缩
*
* @param file
* 待压缩文件
* @param zos
* ZipOutputStream
* @param dir
* 压缩文件中的当前路径
* @throws Exception
*/
private static void compressFile(File file, ZipOutputStream zos, String dir)
throws Exception {
/**
* 压缩包内文件名定义
*
* 如果有多级目录,那么这里就需要给出包含目录的文件名 如果用WinRAR打开压缩包,中文名将显示为乱码
*/
if ("/".equals(dir))
dir = "";
else if (dir.startsWith("/"))
dir = dir.substring(1, dir.length());
ZipEntry entry = new ZipEntry(dir + file.getName());
zos.putNextEntry(entry);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
file));
int count;
byte data[] = new byte[BUFFER];
while ((count = bis.read(data, 0, BUFFER)) != -1) {
zos.write(data, 0, count);
}
bis.close();
zos.closeEntry();
}
public static void main(String[] args) throws Exception {
StringBuffer buffer = new StringBuffer();
BufferedReader br = null;
OutputStreamWriter osw = null;
String srcPath = "C:\\Users\\Jakieenchan\\Desktop\\Analyse_Apk\\aa\\TestPrint.APK";
String content = "channel_id=LD20120926";
File srcFile = new File(srcPath);
String parentPath = srcFile.getParent(); // 源文件目录
String fileName = srcFile.getName(); // 源文件名称
String prefixName = fileName.substring(0, fileName.lastIndexOf("."));//文件前缀名
// 解压源文件保存路径
String sourcePath = buffer.append(parentPath).append(SOURCE_PATH_NAME)
.append(prefixName).append("\\").toString();
// ------解压
unZip(srcPath, sourcePath);
// ------删除解压后的签名文件
String signPathName = sourcePath + SIGN_PATH_NAME;
File signFile = new File(signPathName);
if (signFile.exists()) {
File sonFiles[] = signFile.listFiles();
if (sonFiles != null && sonFiles.length > 0) {
// 循环删除签名目录下的文件
for (File f : sonFiles) {
f.delete();
}
}
signFile.delete();
}
// -----------------------------------------------------------------------
// --------- 如果有需要可在此处填写修改APK内容的代码 -------------
// -----------------------------------------------------------------------
// ------打包
String targetPath = parentPath + TARGET_PATH_NAME;
// 判断创建文件夹
File targetFile = new File(targetPath);
if (!targetFile.exists()) {
targetFile.mkdir();
}
String compressSrcFile = parentPath + SOURCE_PATH_NAME + prefixName;
String compressDesPath = targetPath + fileName;
compress(compressSrcFile, compressDesPath);
// ------签名
File ff = new File(JDK_BIN_PATH);
String resultPath = parentPath + RESULT_PATH_NAME;
// 判断创建文件夹
File resultFile = new File(resultPath);
if (!resultFile.exists()) {
resultFile.mkdir();
}
// 组合签名命令
buffer.setLength(0);
buffer.append("cmd.exe /c jarsigner -keystore ")
.append(SECRET_KEY_PATH).append(SECRET_KEY_NAME)
.append(" -storepass 111111 -signedjar ").append(resultPath)
.append(fileName).append(" ") // 签名保存路径应用名称
.append(targetPath).append(fileName).append(" ") // 打包保存路径应用名称
.append(SECRET_KEY_NAME);
// 利用命令调用JDK工具命令进行签名
Process process = Runtime.getRuntime()
.exec(buffer.toString(), null, ff);
if (process.waitFor() != 0){
System.out.println("文件打包失败!!!");
}else{
System.out.println("--文件打包成功!!!!--");
}
}
}
注意
1.实际使用中,变量JDK_BIN_PATH、SECRET_KEY_PATH、SECRET_KEY_NAME请按照个人情况修改。
2.组合签名命令部分中的“111111”是指keystore的密钥库口令,请按照个人实际情况修改。
3.关于密钥文件keystore,可参考本人的另一篇文章https://blog.csdn.net/qq_25844803/article/details/84953332
4.以上方法不能对apk的android代码(程序)进行修改,只能修改类似资源文件,布局文件