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

本文转自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文件
主要用到的核心代码:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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   

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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.   
  9.             String oldFile = sd + "/aDiff/old.apk";  
  10.             String newFile = sd + "/aDiff/new.apk";  
  11.             String patchFile = sd + "/aDiff/old_new.patch";  
  12.   
  13.             DiffWriter output = null;  
  14.             File sourceFile = null;  
  15.             File targetFile = null;  
  16.   
  17.             sourceFile = new File(oldFile);  
  18.             targetFile = new File(newFile);  
  19.             output = new GDiffWriter(new DataOutputStream(  
  20.                     new BufferedOutputStream(new FileOutputStream(new File(  
  21.                             patchFile)))));  
  22.   
  23.             if (sourceFile.length() > Integer.MAX_VALUE  
  24.                     || targetFile.length() > Integer.MAX_VALUE) {  
  25.                 System.err  
  26.                         .println("source or target is too large, max length is "  
  27.                                 + Integer.MAX_VALUE);  
  28.                 System.err.println("aborting..");  
  29.   
  30.             }  
  31.   
  32.             Delta d = new Delta();  
  33.             d.compute(sourceFile, targetFile, output);  
  34.   
  35.             Toast.makeText(getApplicationContext(), "生成完成!", Toast.LENGTH_LONG)  
  36.                     .show();  
  37.         } catch (Exception e) {  
  38.             e.printStackTrace();  
  39.         }  
  40.     }  
2、合成差分包

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

核心代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      * 合成差分包:new.apk = old.apk + old_new.patch 
  3.      * */  
  4.     private void mixPatch() {  
  5.         try {  
  6.   
  7.             String sd = Environment.getExternalStorageDirectory()  
  8.                     .getAbsolutePath();  
  9.             String serviceFile = sd + "/aDiff/new.apk";  
  10.             String source = sd + "/aDiff/old.apk";  
  11.             String patch = sd + "/aDiff/old_new.patch";  
  12.   
  13.             String target = sd + "/aDiff/mix.apk";  
  14.   
  15.             String newMD5 = DiffTool.getMD5(new File(serviceFile));  
  16.   
  17.             DiffTool.mergeApk(source, patch, target, newMD5);  
  18.   
  19.             Toast.makeText(getApplicationContext(), "合成完成!", Toast.LENGTH_LONG)  
  20.                     .show();  
  21.         } catch (Exception e) {  
  22.             e.printStackTrace();  
  23.         }  
  24.     }  
  25.   
  26. private static File mergeFile(final String source, final String patch,  
  27.             String target) throws Exception {  
  28.         GDiffPatcher patcher = new GDiffPatcher();  
  29.         File deffFile = new File(patch);  
  30.         File updatedFile = new File(target);  
  31.         patcher.patch(new File(source), deffFile, updatedFile);  
  32.         return updatedFile;  
  33.     }  
  34.   
  35.     public static File mergeApk(final String source, final String patch,  
  36.             final String target, String newApkMd5) throws Exception {  
  37.         File updateFile = mergeFile(source, patch, target);  
  38.         String ufpMd5 = getMD5(updateFile);  
  39.         System.out  
  40.                 .println("服务端下发的md5:" + newApkMd5 + ",新合并后的apk MD5:" + ufpMd5);  
  41.         if (ufpMd5 == null || !newApkMd5.equalsIgnoreCase(ufpMd5)) {  
  42.             if (updateFile.exists()) {  
  43.                 updateFile.delete();  
  44.             }  
  45.             throw new Exception("MD5错误,不能成功合并!");  
  46.         }  
  47.   
  48.         return updateFile;  
  49.     } 

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

3、安装apk

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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.   
  16.     }  

注释:
  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 plain copy 在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.   
  15.                 sourceFile = new java.io.File(oldApkURL);  
  16.                 targetFile = new java.io.File(newApkUrl);  
  17.   
  18.                 if (sourceFile.exists() && targetFile.exists())  
  19.                 {  
  20.   
  21.                     output = new com.nothome.delta.GDiffWriter(new java.io.DataOutputStream(  
  22.                             new java.io.BufferedOutputStream(new java.io.FileOutputStream(new java.io.File(  
  23.                                     patchFileUrl)))));  
  24.   
  25.                     if (sourceFile.length() > int.MaxValue  
  26.                             || targetFile.length() > int.MaxValue)  
  27.                     {  
  28.                     }  
  29.   
  30.                     com.nothome.delta.Delta d = new com.nothome.delta.Delta();  
  31.                     d.compute(sourceFile, targetFile, output);  
  32.   
  33.                     return true;  
  34.                 }  
  35.                 else  
  36.                 {  
  37.   
  38.                     this.ShowMessage("源文件不存在!");  
  39.   
  40.                     return false;  
  41.                 }  
  42.             }  
  43.             catch (Exception e)  
  44.             {  
  45.                 return false;  
  46.             }  
  47.         }  


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

源码下载: Android增量更新


猜你喜欢

转载自blog.csdn.net/SouthRR/article/details/45895163
今日推荐