Android 版本更新之增量更新 包含java、.net服务端

论坛 期权论坛 脚本     
匿名技术用户   2020-12-23 17:14   28   0

本文转自http://blog.csdn.net/u013705351/article/details/22722499

@hmg25编写的文章《浅析android应用增量升级》详细描述了增量更新的原理。简单来说,增量更新步骤如下:

  1. 准备新旧两个版本的apk(A,B);
  2. 对A,B进行差分比较,并生成差分包(diff(A,B) => patch),同时生成B的MD5;
  3. 新apk的合成:B = A + patch,新合成包的MD5和服务端更新下来的MD5进行比对,相同即可安装。
假设A 4m,B 5m,在服务端生成A —> B的差分包为 B-A = C1m(举个例子而已,可能实际变化的部分不止1M),客户端在更新的时候,将文件C下载,C与旧版A合成新的安装包D,校验B和D的MD5,若相同,则安装,否则更新失败。

demo运行过程中,所产生的文件:


在编码代码之前,需要做一些准备工作:
  1. 新旧apk(A,B)的准备。在demo中,提供old.apk、new.apk。
  2. 差分包生成和合并的jar包。javaxdelta.jar trove.jar
1. 生成patch文件
主要用到的核心代码:
  1. /**
  2. * @param sourceFile 旧版本文件(.apk)
  3. * @param targetFile 新版本文件(.apk)
  4. * @param output 输出文件(.patch)
  5. * */
  6. com.nothome.delta.Delta.compute(File sourceFile, File targetFile, DiffWriter output) throws IOException

  1. /**
  2. * 生成差分包:old_new.patch = diff(old.apk, old.apk)
  3. *
  4. * */
  5. private void createPatch() {
  6. try {
  7. String sd = Environment.getExternalStorageDirectory().getPath();
  8. String oldFile = sd + "/aDiff/old.apk";
  9. String newFile = sd + "/aDiff/new.apk";
  10. String patchFile = sd + "/aDiff/old_new.patch";
  11. DiffWriter output = null;
  12. File sourceFile = null;
  13. File targetFile = null;
  14. sourceFile = new File(oldFile);
  15. targetFile = new File(newFile);
  16. output = new GDiffWriter(new DataOutputStream(
  17. new BufferedOutputStream(new FileOutputStream(new File(
  18. patchFile)))));
  19. if (sourceFile.length() > Integer.MAX_VALUE
  20. || targetFile.length() > Integer.MAX_VALUE) {
  21. System.err
  22. .println("source or target is too large, max length is "
  23. + Integer.MAX_VALUE);
  24. System.err.println("aborting..");
  25. }
  26. Delta d = new Delta();
  27. d.compute(sourceFile, targetFile, output);
  28. Toast.makeText(getApplicationContext(), "生成完成!", Toast.LENGTH_LONG)
  29. .show();
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. }
  33. }
2、合成差分包

思路:将patch和旧版本文件合成,并比对MD5,若生成文件的MD5与服务器下发的MD5匹配,则提示合成成功,否则删除该文件。

核心代码:

  1. /**
  2. * 合成
  3. * @param sourceFile 旧版本文件
  4. * @param patchFile 更新包
  5. * @param outputFile 新版本文件(生成)
  6. *
  7. * */
  8. com.nothome.delta.GDiffPatcher.patch(File sourceFile, File patchFile, File outputFile) throws IOException
  1. /**
  2. * 合成差分包:new.apk = old.apk + old_new.patch
  3. * */
  4. private void mixPatch() {
  5. try {
  6. String sd = Environment.getExternalStorageDirectory()
  7. .getAbsolutePath();
  8. String serviceFile = sd + "/aDiff/new.apk";
  9. String source = sd + "/aDiff/old.apk";
  10. String patch = sd + "/aDiff/old_new.patch";
  11. String target = sd + "/aDiff/mix.apk";
  12. String newMD5 = DiffTool.getMD5(new File(serviceFile));
  13. DiffTool.mergeApk(source, patch, target, newMD5);
  14. Toast.makeText(getApplicationContext(), "合成完成!", Toast.LENGTH_LONG)
  15. .show();
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. private static File mergeFile(final String source, final String patch,
  21. String target) throws Exception {
  22. GDiffPatcher patcher = new GDiffPatcher();
  23. File deffFile = new File(patch);
  24. File updatedFile = new File(target);
  25. patcher.patch(new File(source), deffFile, updatedFile);
  26. return updatedFile;
  27. }
  28. public static File mergeApk(final String source, final String patch,
  29. final String target, String newApkMd5) throws Exception {
  30. File updateFile = mergeFile(source, patch, target);
  31. String ufpMd5 = getMD5(updateFile);
  32. System.out
  33. .println("服务端下发的md5:" + newApkMd5 + ",新合并后的apk MD5:" + ufpMd5);
  34. if (ufpMd5 == null || !newApkMd5.equalsIgnoreCase(ufpMd5)) {
  35. if (updateFile.exists()) {
  36. updateFile.delete();
  37. }
  38. throw new Exception("MD5错误,不能成功合并!");
  39. }
  40. return updateFile;
  41. }

接下来就是最后一步,安装APK。

3、安装apk

  1. /**
  2. * 安装apk。 这边路径已经写死,实际应用中,apk路径需要当参数传入
  3. * */
  4. private void installAPK() {
  5. File apkfile = new File(Environment.getExternalStorageDirectory()
  6. .getAbsolutePath() + "/aDiff/mix.apk");
  7. if (!apkfile.exists()) {
  8. return;
  9. }
  10. Intent i = new Intent(Intent.ACTION_VIEW);
  11. i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  12. i.setDataAndType(Uri.parse("file://" + apkfile.toString()),
  13. "application/vnd.android.package-archive");
  14. MainActivity.this.startActivity(i);
  15. }

注释:
  1. 在该demo中,路径都是写死的,在实际应用中,应在上述三个关键方法中,设置路径参数。
  2. 差分包的生成、合成都没有写在异步方法里面。为了提高用户体验,应编写相应的异步方法,如run、async等。
  3. 差分包的生成写在了客户端,实际应该是放在服务端。如果服务端用java来写,那么很幸运,代码直接复制即可。下面我将提供服务端为.NET的差分包生成方式。其他语言没有做研究,额额。。
4、.NET服务端的.patch文件生成
同样是调用javaxdelta,trove,当然并不是直接调用jar包,首先要将这两个包编译成dll文件,供.NET调用。在这里感谢博客园的一位朋友,详细的讲解了IKVM的使用方法:@xiaotie的博文将java库转换为.net库 在这就不详细介绍了。在转换过程中,若遇到问题,可以给我留言,嘿嘿~~

通过IKVM这个工具,将上述两个jar包转成对应的dll文件:javaxdelta.dll、trove.dll,只有这两个包是不够的,还需要将IKVM.OpenJDK.Core.dll、IKVM.Runtime.dll、IKVM.Runtime.JNI.dll三个类库同时引入,才可以正常编译。
.NET服务端.patch生成代码如下:
[csharp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /// <summary>
  2. /// 生成差分包
  3. /// </summary>
  4. /// <param name="oldApkURL"></param>
  5. /// <param name="newApkUrl"></param>
  6. /// <param name="patchFileUrl"></param>
  7. private bool CreateFile(string oldApkURL, string newApkUrl, string patchFileUrl)
  8. {
  9. try
  10. {
  11. com.nothome.delta.DiffWriter output = null;
  12. java.io.File sourceFile = null;
  13. java.io.File targetFile = null;
  14. sourceFile = new java.io.File(oldApkURL);
  15. targetFile = new java.io.File(newApkUrl);
  16. if (sourceFile.exists() && targetFile.exists())
  17. {
  18. output = new com.nothome.delta.GDiffWriter(new java.io.DataOutputStream(
  19. new java.io.BufferedOutputStream(new java.io.FileOutputStream(new java.io.File(
  20. patchFileUrl)))));
  21. if (sourceFile.length() > int.MaxValue
  22. || targetFile.length() > int.MaxValue)
  23. {
  24. }
  25. com.nothome.delta.Delta d = new com.nothome.delta.Delta();
  26. d.compute(sourceFile, targetFile, output);
  27. return true;
  28. }
  29. else
  30. {
  31. this.ShowMessage("源文件不存在!");
  32. return false;
  33. }
  34. }
  35. catch (Exception e)
  36. {
  37. return false;
  38. }
  39. }


通过上述操作,就可以实现增量更新。额额。。文件下载什么的好像还没有加撒~这部分的内容可以直接参考度娘的写法即可了~嘿嘿~

源码下载:Android增量更新


分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP