以补丁的方式来更新应用程序

创建补丁:

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 --> 点我呀点我呀

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值