创建补丁:
public class CrpMain extends JFrame {
// private static JFrame frame;
private String oldPath = "", newPath = "", outPath = "";
private static int OLD = 1, NEW = 2;
private JTextField field1, field2, field3;
public static void main(String[] args) {
CrpMain m = new CrpMain();
m.show();
}
public CrpMain() {
// TODO 自动生成的构造函数存根
this.setTitle("APK补丁生成器");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setMinimumSize(new Dimension(300, 300));
this.setLocation(100, 100);
this.setResizable(false);
Container cp = this.getContentPane();
cp.setLayout(null);
field1 = new JTextField();
field1.setEditable(false);
field1.setText("请选择*旧*版本APK");
field2 = new JTextField();
field2.setEditable(false);
field2.setText("请选择*新*版本APK");
field3 = new JTextField();
field3.setEditable(false);
field3.setText("请选择输出位置");
// create button
JButton b1 = new JButton("选择");
JButton b2 = new JButton("选择");
JButton b3 = new JButton("选择");
JButton b4 = new JButton("输出");
b1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO 自动生成的方法存根
showChooser(OLD);
}
});
b2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO 自动生成的方法存根
showChooser(NEW);
}
});
b3.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO 自动生成的方法存根
showFloderChooser();
}
});
b4.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO 自动生成的方法存根
if (oldPath.equals("") || newPath.equals("")
|| outPath.equals("")) {
JOptionPane.showMessageDialog(null, "请完善文件位置与目录信息", "警告!",
JOptionPane.ERROR_MESSAGE);
return;
}
createPatch(oldPath, newPath, outPath);
}
});
cp.add(field1);
field1.setBounds(10, 10, 270, 30);
cp.add(b1);
b1.setBounds(180, 50, 100, 30);
cp.add(field2);
field2.setBounds(10, 90, 270, 30);
cp.add(b2);
b2.setBounds(180, 130, 100, 30);
cp.add(field3);
field3.setBounds(10, 170, 270, 30);
cp.add(b3);
b3.setBounds(180, 210, 100, 30);
cp.add(b4);
b4.setBounds(10, 210, 100, 30);
// show the window
this.pack();
this.setVisible(true);
}
/**
* 选择输出目录
*/
private void showFloderChooser() {
JFileChooser chooser = new JFileChooser("../");
String selectPath = "";
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);// 设置只能选择目录
int returnVal = chooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
selectPath = chooser.getSelectedFile().getPath();
// System.out.println("你选择的目录是:" + selectPath);
outPath = selectPath;
field3.setText(outPath);
chooser.hide();
}
}
/**
* 选择文件
*
* @param which
*/
private void showChooser(int which) {
JFileChooser chooser = new JFileChooser("../");
FileNameExtensionFilter filter = new FileNameExtensionFilter(
"Android程序", "apk");
chooser.setFileFilter(filter);
String selectPath = "";
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);// 设置只能选择文件
int returnVal = chooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
selectPath = chooser.getSelectedFile().getPath();
// System.out.println("你选择的文件是:" + selectPath);
if (which == NEW) {
newPath = selectPath;
field2.setText(newPath);
} else {
oldPath = selectPath;
field1.setText(oldPath);
}
chooser.hide();
}
}
/**
* 创建补丁
*/
private void createPatch(String oldPath, String newPath, String outPath) {
try {
String oldFile = oldPath;
String newFile = newPath;
String patchFile = outPath + "/p.patch";
DiffWriter output = null;
File sourceFile = null;
File targetFile = null;
sourceFile = new File(oldFile);
targetFile = new File(newFile);
output = new GDiffWriter(new DataOutputStream(
new BufferedOutputStream(new FileOutputStream(new File(
patchFile)))));
if (sourceFile.length() > Integer.MAX_VALUE
|| targetFile.length() > Integer.MAX_VALUE) {
System.err
.println("source or target is too large, max length is "
+ Integer.MAX_VALUE);
System.err.println("aborting..");
}
Delta d = new Delta();
d.compute(sourceFile, targetFile, output);
JOptionPane.showMessageDialog(null, "补丁生成完成!", "提示",
JOptionPane.DEFAULT_OPTION);
} catch (Exception e) {
e.printStackTrace();
}
}
}
合成补丁与应用程序:
public class MixMain extends JFrame {
/**
*
*/
private static final long serialVersionUID = 6350910151842747382L;
// private static JFrame frame;
private String oldPath = "", newPath = "", outPath = "";
private static int OLD = 1, NEW = 2;
private JTextField field1, field2, field3;
public static void main(String[] args) {
MixMain m = new MixMain();
m.show();
}
public MixMain() {
// TODO 自动生成的构造函数存根
this.setTitle("APK补丁合成器");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setMinimumSize(new Dimension(300, 300));
this.setLocation(100, 100);
this.setResizable(false);
Container cp = this.getContentPane();
cp.setLayout(null);
field1 = new JTextField();
field1.setEditable(false);
field1.setText("请选择*旧*版本APK");
field2 = new JTextField();
field2.setEditable(false);
field2.setText("请选择*补丁*文件");
field3 = new JTextField();
field3.setEditable(false);
field3.setText("请选择输出位置");
// create button
JButton b1 = new JButton("选择");
JButton b2 = new JButton("选择");
JButton b3 = new JButton("选择");
JButton b4 = new JButton("输出");
b1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO 自动生成的方法存根
showChooser(OLD);
}
});
b2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO 自动生成的方法存根
showChooser(NEW);
}
});
b3.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO 自动生成的方法存根
showFloderChooser();
}
});
b4.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO 自动生成的方法存根
if (oldPath.equals("") || newPath.equals("")
|| outPath.equals("")) {
JOptionPane.showMessageDialog(null, "请完善文件位置与目录信息", "警告!",
JOptionPane.ERROR_MESSAGE);
return;
}
mixPatch(oldPath, newPath, outPath);
}
});
cp.add(field1);
field1.setBounds(10, 10, 270, 30);
cp.add(b1);
b1.setBounds(180, 50, 100, 30);
cp.add(field2);
field2.setBounds(10, 90, 270, 30);
cp.add(b2);
b2.setBounds(180, 130, 100, 30);
cp.add(field3);
field3.setBounds(10, 170, 270, 30);
cp.add(b3);
b3.setBounds(180, 210, 100, 30);
cp.add(b4);
b4.setBounds(10, 210, 100, 30);
// show the window
this.pack();
this.setVisible(true);
}
/**
* 选择输出目录
*/
private void showFloderChooser() {
JFileChooser chooser = new JFileChooser("../");
String selectPath = "";
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);// 设置只能选择目录
int returnVal = chooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
selectPath = chooser.getSelectedFile().getPath();
// System.out.println("你选择的目录是:" + selectPath);
outPath = selectPath;
field3.setText(outPath);
chooser.hide();
}
}
/**
* 选择文件
*
* @param which
*/
private void showChooser(int which) {
JFileChooser chooser = new JFileChooser("../");
FileNameExtensionFilter filter = new FileNameExtensionFilter(
"Android程序", "apk");
if (which == NEW) {
filter = new FileNameExtensionFilter("补丁文件", "patch");
}
chooser.setFileFilter(filter);
String selectPath = "";
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);// 设置只能选择文件
int returnVal = chooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
selectPath = chooser.getSelectedFile().getPath();
// System.out.println("你选择的文件是:" + selectPath);
if (which == NEW) {
newPath = selectPath;
field2.setText(newPath);
} else {
oldPath = selectPath;
field1.setText(oldPath);
}
chooser.hide();
}
}
/**
* 合成
*/
private void mixPatch(String oldPath, String newPath, String outPath) {
try {
// String serviceFile = sd + "/test2.apk";
String source = oldPath;
String patch = newPath;
String target = outPath + "/new.apk";
// String newMD5 = DiffTool.getMD5(new File(serviceFile));
DiffTool.mergeApk(source, patch, target, "");
JOptionPane.showMessageDialog(null, "合成完毕!", "提示",
JOptionPane.DEFAULT_OPTION);
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "出现异常!\n" + e.getMessage(),
"警告!", JOptionPane.ERROR_MESSAGE);
}
}
}
DiffTool.java :
public class DiffTool {
private final static char[] hexChar = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
private static String toHexString(byte[] b) {
StringBuilder sb = new StringBuilder(b.length * 2);
for (int i = 0; i < b.length; i++) {
sb.append(hexChar[((b[i] & 0xF0) >>> 4)]);
sb.append(hexChar[(b[i] & 0xF)]);
}
return sb.toString();
}
public static String getMD5(File file) {
InputStream fis = null;
String str = null;
try {
fis = new FileInputStream(file);
byte[] buffer = new byte[1024];
MessageDigest md5 = MessageDigest.getInstance("MD5");
int numRead = 0;
while ((numRead = fis.read(buffer)) > 0) {
md5.update(buffer, 0, numRead);
}
str = toHexString(md5.digest());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (Exception e) {
}
}
}
return str;
}
/**
* 合并文件
*
* @param source
* 旧的APK
* @param patch
* 补丁文件
* @param target
* 生成的新APK的路径
* @return
* @throws Exception
*/
private static File mergeFile(final String source, final String patch,
String target) throws Exception {
GDiffPatcher patcher = new GDiffPatcher();
File deffFile = new File(patch);
File updatedFile = new File(target);
patcher.patch(new File(source), deffFile, updatedFile);
return updatedFile;
}
/**
* 合并出APK并且进行MD5值验证
*
* @param source
* @param patch
* @param target
* @param newApkMd5 暂时移除
* @return
* @throws Exception
*/
public static File mergeApk(final String source, final String patch,
final String target, String newApkMd5) throws Exception {
File updateFile = mergeFile(source, patch, target);
// String ufpMd5 = getMD5(updateFile);
// System.out
// .println("服务端下发的md5:" + newApkMd5 + ",新合并后的apk MD5:" + ufpMd5);
// if (ufpMd5 == null || !newApkMd5.equalsIgnoreCase(ufpMd5)) {
// if (updateFile.exists()) {
// updateFile.delete();
// }
// throw new Exception("MD5错误,不能成功合并!");
// }
return updateFile;
}
// public static void main(String args[]) throws Exception {
//
// try {
// System.out.println("old Md5:"
// + DiffTool.getMD5(new File("d:/diff/appstore2024.apk")));
// File sourceFile = new File("d:/diff/appstore2017.apk");
// File patchFile = new File("d:/diff/appstore.patch");
// File outputFile = new File("d:/diff/appstore2025.apk");
//
// if (sourceFile.length() > Integer.MAX_VALUE
// || patchFile.length() > Integer.MAX_VALUE) {
// System.err
// .println("source or patch is too large, max length is "
// + Integer.MAX_VALUE);
// System.err.println("aborting..");
// return;
// }
// GDiffPatcher patcher = new GDiffPatcher();
// patcher.patch(sourceFile, patchFile, outputFile);
//
// System.out.println("finished patching file");
//
// } catch (Exception ioe) { // gls031504a
// System.err.println("error while patching: " + ioe);
// }
//
// System.out.println("new Md5:"
// + DiffTool.getMD5(new File("d:/diff/appstore2025.apk")));
//
// }
}
jars --> 点我呀点我呀