Android增量更新(四)-客户端合并差分包生成新的apk安装包

1,836 阅读23分钟
原文链接: blog.csdn.net

目录:


        Android增量更新(一)-差分文件(Windows-part1)

        Android增量更新(二)-差分文件(Windows-part2)-dll动态库和jar包

        Android增量更新(三)-差分文件(Linux)-生成jar和.so库

        Android增量更新(四)-客户端合并差分包生成新的apk安装包

        前面已经实现了在服务器端(Windows和LLinux)生成差分包,并生成了相应的jar包和动态库,方便直接使用。增量更新算是完成了一半了,还差客户端对差分包的合并,现在就来介绍如何在客户端合并差分包生成新的apk安装包。

        合并差分包主要用到的是:bspatch.c(linux)和bzip2的源码。所以总的来说也不是很难。


一、创建Android项目(BsPatchYwl5320)

1.1、创建C++的Android项目,点击next直到完成。



1.2、添加NDK路径



1.3、编译并成功运行就表示C++的Android项目成功搭建好了



二、配置bspatch和bzip2源码并编译

2.1、删除系统生成的本地方法和c++文件,并添加bspatch.c和bzip2源码



2.2、修改CMakeLists.txt配置来编译c代码并设置生成.so库的名称(BsPatchYwl5320)

2.2.1、添加bspatch.c和bzip2中的.c路径有2中方法。

第一种是在add_library中添加所以.c路径,如:

  1. add_library( # Sets the name of the library.  
  2.              BsPatchYwl5320  
  3.   
  4.              # Sets the library as a shared library.  
  5.              SHARED  
  6.   
  7.              # Provides a relative path to your source file(s).  
  8.              src/main/cpp/bspatch.c  
  9.              src/main/cpp/bzip2/blocksort.c  
  10.              src/main/cpp/bzip2/bzip2.c  
  11.              ... #添加完所有.c文件  
  12.              )  
add_library( # Sets the name of the library.
             BsPatchYwl5320

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/bspatch.c
             src/main/cpp/bzip2/blocksort.c
             src/main/cpp/bzip2/bzip2.c
             ... #添加完所有.c文件
             )
这种方法对源文件个数较少的时候适用,当个数多了时,既难写又容易出错。


第二种方法就简单了,把包含源文件的目录设置成一个全局变量,然后指定读取里面的.c就行了,如:

  1. file(GLOB bzip_c src/main/cpp/bzip2/*.c)  
  2.   
  3. add_library( # Sets the name of the library.  
  4.              BsPatchYwl5320  
  5.   
  6.              # Sets the library as a shared library.  
  7.              SHARED  
  8.   
  9.              # Provides a relative path to your source file(s).  
  10.              ${bzip_c}  
  11.              src/main/cpp/bspatch.c  
  12.              )  
file(GLOB bzip_c src/main/cpp/bzip2/*.c)

add_library( # Sets the name of the library.
             BsPatchYwl5320

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             ${bzip_c}
             src/main/cpp/bspatch.c
             )



2.2.2、引入动态库BsPatchYwl5320

  1. target_link_libraries( # Specifies the target library.  
  2.                        BsPatchYwl5320  
  3.   
  4.                        # Links the target library to the log library  
  5.                        # included in the NDK.  
  6.                        ${log-lib} )  
target_link_libraries( # Specifies the target library.
                       BsPatchYwl5320

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )


2.2.3、因为bzip2路径改变了,所以需要修改bspatch.c中#include <bzlib.h> 为 #include "bzip2/bzlib.h"

2.2.4、由于.c中规定只能有一个main方法而bspatch.c中已经有了main方法,所以得查找bzip2中的所有文件中是否含有main方法,如果有就把main方法给改成别的,这里以"文件名_main"的方式更改。

2.2.5、修改好后编译一下,能成功编译则表示源码已经添加配置完成了,如:



三、添加native方法

3.1、编写BsPathYwl5320Util.java类

  1. package com.ywl5320.bspatchywl5320;  
  2.   
  3. /** 
  4.  * Created by ywl5320 on 2017/10/25. 
  5.  */  
  6.   
  7. public class BsPatchYwl5320Util {  
  8.   
  9.     private static BsPatchYwl5320Util instance = new BsPatchYwl5320Util();  
  10.   
  11.     private BsPatchYwl5320Util(){}  
  12.   
  13.     public static BsPatchYwl5320Util getInstance()  
  14.     {  
  15.         return instance;  
  16.     }  
  17.   
  18.     static  
  19.     {  
  20.         System.loadLibrary("BsPatchYwl5320");  
  21.     }  
  22.   
  23.     public native int bsPatch(String oldfile, String newfile, String patchfile);  
  24.   
  25. }  
package com.ywl5320.bspatchywl5320;

/**
 * Created by ywl5320 on 2017/10/25.
 */

public class BsPatchYwl5320Util {

    private static BsPatchYwl5320Util instance = new BsPatchYwl5320Util();

    private BsPatchYwl5320Util(){}

    public static BsPatchYwl5320Util getInstance()
    {
        return instance;
    }

    static
    {
        System.loadLibrary("BsPatchYwl5320");
    }

    public native int bsPatch(String oldfile, String newfile, String patchfile);

}

3.2、编译项目生成.class文件,然后用javah命令生成.h文件

  1. javah com.ywl5320.bspatchywl5320.BsPatchYwl5320Util  
javah com.ywl5320.bspatchywl5320.BsPatchYwl5320Util



3.3.、移动com_ywl5320_bspatchywl5320_BsPatchYwl5320Util.h到cpp文件下,并在bspatch.c中实现native方法,然后添加Android日志打印功能,并修改原来err方法,最终bspatch.c代码如下:

  1. /*- 
  2.  * Copyright 2003-2005 Colin Percival 
  3.  * All rights reserved 
  4.  * 
  5.  * Redistribution and use in source and binary forms, with or without 
  6.  * modification, are permitted providing that the following conditions  
  7.  * are met: 
  8.  * 1. Redistributions of source code must retain the above copyright 
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  * 2. Redistributions in binary form must reproduce the above copyright 
  11.  *    notice, this list of conditions and the following disclaimer in the 
  12.  *    documentation and/or other materials provided with the distribution. 
  13.  * 
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
  15.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
  16.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
  17.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 
  18.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
  19.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
  20.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
  21.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
  22.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
  23.  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
  24.  * POSSIBILITY OF SUCH DAMAGE. 
  25.  */  
  26.   
  27. #if 0  
  28. __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $");  
  29. #endif  
  30.   
  31. #include "com_ywl5320_bspatchywl5320_BsPatchYwl5320Util.h"  
  32. #include "bzip2/bzlib.h"  
  33. #include <stdlib.h>  
  34. #include <stdio.h>  
  35. #include <string.h>  
  36. //#include <err.h>  
  37. #include <unistd.h>  
  38. #include <fcntl.h>  
  39.   
  40. //打印日志  
  41. #include <android/log.h>  
  42. #define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"ywl5320",FORMAT,##__VA_ARGS__);  
  43.   
  44. static off_t offtin(u_char *buf)  
  45. {  
  46.     off_t y;  
  47.   
  48.     y=buf[7]&0x7F;  
  49.     y=y*256;y+=buf[6];  
  50.     y=y*256;y+=buf[5];  
  51.     y=y*256;y+=buf[4];  
  52.     y=y*256;y+=buf[3];  
  53.     y=y*256;y+=buf[2];  
  54.     y=y*256;y+=buf[1];  
  55.     y=y*256;y+=buf[0];  
  56.   
  57.     if(buf[7]&0x80) y=-y;  
  58.   
  59.     return y;  
  60. }  
  61.   
  62. int patch_main(int argc,char * argv[])  
  63. {  
  64.     FILE * f, * cpf, * dpf, * epf;  
  65.     BZFILE * cpfbz2, * dpfbz2, * epfbz2;  
  66.     int cbz2err, dbz2err, ebz2err;  
  67.     int fd;  
  68.     ssize_t oldsize,newsize;  
  69.     ssize_t bzctrllen,bzdatalen;  
  70.     u_char header[32],buf[8];  
  71.     u_char *old, *new;  
  72.     off_t oldpos,newpos;  
  73.     off_t ctrl[3];  
  74.     off_t lenread;  
  75.     off_t i;  
  76.   
  77.     if(argc!=4)  
  78.     {  
  79.         LOGI("usage: %s oldfile newfile patchfile\n",argv[0])  
  80.         return 1;  
  81.     }  
  82. //        errx(1,);  
  83.   
  84.     /* Open patch file */  
  85.     if ((f = fopen(argv[3], "r")) == NULL)  
  86.     {  
  87.         LOGI("can't find patch file: %s", argv[3]);  
  88.         return 1;  
  89.     }  
  90.   
  91.   
  92.     /* 
  93.     File format: 
  94.         0   8   "BSDIFF40" 
  95.         8   8   X 
  96.         16  8   Y 
  97.         24  8   sizeof(newfile) 
  98.         32  X   bzip2(control block) 
  99.         32+X    Y   bzip2(diff block) 
  100.         32+X+Y  ??? bzip2(extra block) 
  101.     with control block a set of triples (x,y,z) meaning "add x bytes 
  102.     from oldfile to x bytes from the diff block; copy y bytes from the 
  103.     extra block; seek forwards in oldfile by z bytes". 
  104.     */  
  105.   
  106.     /* Read header */  
  107.     if (fread(header, 1, 32, f) < 32) {  
  108.         if (feof(f))  
  109.         {  
  110.             LOGI("Corrupt patch");  
  111.             return 1;  
  112.         }  
  113.         LOGI("can't read patchfile header: %s", argv[3]);  
  114.         return 1;  
  115.     }  
  116.   
  117.     /* Check for appropriate magic */  
  118.     if (memcmp(header, "BSDIFF40", 8) != 0) {  
  119.         LOGI("Corrupt patch\n");  
  120.         return 1;  
  121.     }  
  122.   
  123.     /* Read lengths from header */  
  124.     bzctrllen=offtin(header+8);  
  125.     bzdatalen=offtin(header+16);  
  126.     newsize=offtin(header+24);  
  127.     if((bzctrllen<0) || (bzdatalen<0) || (newsize<0)) {  
  128.         LOGI("Corrupt patch\n");  
  129.         return 1;  
  130.     }  
  131.   
  132.     /* Close patch file and re-open it via libbzip2 at the right places */  
  133.     if (fclose(f)) {  
  134.         LOGI("%s", argv[3]);  
  135.         return 1;  
  136.     }  
  137.     if ((cpf = fopen(argv[3], "r")) == NULL) {  
  138.         LOGI("%s", argv[3]);  
  139.         return 1;  
  140.     }  
  141.     if (fseeko(cpf, 32, SEEK_SET)) {  
  142.         LOGI("%s", argv[3]);  
  143.         return 1;  
  144.     }  
  145.     if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL) {  
  146.        LOGI("BZ2_bzReadOpen, bz2err = %d", cbz2err);  
  147.         return 1;  
  148.     }  
  149.     if ((dpf = fopen(argv[3], "r")) == NULL) {  
  150.         LOGI("%s", argv[3]);  
  151.         return 1;  
  152.     }  
  153.     if (fseeko(dpf, 32 + bzctrllen, SEEK_SET)) {  
  154.         LOGI("%s", argv[3]);  
  155.         return 1;  
  156.     }  
  157.     if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL) {  
  158.         LOGI("BZ2_bzReadOpen dbz2err = %d", dbz2err);  
  159.         return 1;  
  160.     }  
  161.     if ((epf = fopen(argv[3], "r")) == NULL) {  
  162.         LOGI("%s", argv[3]);  
  163.         return 1;  
  164.     }  
  165.     if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET)) {  
  166.         LOGI("%s", argv[3]);  
  167.         return 1;  
  168.     }  
  169.     if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL) {  
  170.         LOGI("BZ2_bzReadOpen, bz2err = %d", ebz2err);  
  171.         return 1;  
  172.     }  
  173.   
  174.     if(((fd=open(argv[1],O_RDONLY,0))<0) ||  
  175.         ((oldsize=lseek(fd,0,SEEK_END))==-1) ||  
  176.         ((old=malloc(oldsize+1))==NULL) ||  
  177.         (lseek(fd,0,SEEK_SET)!=0) ||  
  178.         (read(fd,old,oldsize)!=oldsize) ||  
  179.         (close(fd)==-1))  
  180.     {  
  181.         LOGI("can't find oldfile: %s", argv[1]);  
  182.         return 1;  
  183.     }  
  184.     if((new=malloc(newsize+1))==NULL) {  
  185.         LOGI("newsize is NULL");  
  186.         return 1;  
  187.     }  
  188.   
  189.     oldpos=0;newpos=0;  
  190.     while(newpos<newsize) {  
  191.         /* Read control data */  
  192.         for(i=0;i<=2;i++) {  
  193.             lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);  
  194.             if ((lenread < 8) || ((cbz2err != BZ_OK) &&  
  195.                 (cbz2err != BZ_STREAM_END))) {  
  196.                 LOGI("Corrupt patch\n");  
  197.                 return 1;  
  198.             }  
  199.             ctrl[i]=offtin(buf);  
  200.         };  
  201.   
  202.         /* Sanity-check */  
  203.         if(newpos+ctrl[0]>newsize) {  
  204.             LOGI("Corrupt patch\n");  
  205.             return 1;  
  206.         }  
  207.   
  208.         /* Read diff string */  
  209.         lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);  
  210.         if ((lenread < ctrl[0]) ||  
  211.             ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END))) {  
  212.             LOGI("Corrupt patch\n");  
  213.             return 1;  
  214.         }  
  215.   
  216.         /* Add old data to diff string */  
  217.         for(i=0;i<ctrl[0];i++)  
  218.             if((oldpos+i>=0) && (oldpos+i<oldsize))  
  219.                 new[newpos+i]+=old[oldpos+i];  
  220.   
  221.         /* Adjust pointers */  
  222.         newpos+=ctrl[0];  
  223.         oldpos+=ctrl[0];  
  224.   
  225.         /* Sanity-check */  
  226.         if(newpos+ctrl[1]>newsize) {  
  227.             LOGI("Corrupt patch\n");  
  228.             return 1;  
  229.         }  
  230.   
  231.         /* Read extra string */  
  232.         lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);  
  233.         if ((lenread < ctrl[1]) ||  
  234.             ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END))) {  
  235.             LOGI("Corrupt patch\n");  
  236.             return 1;  
  237.         }  
  238.   
  239.         /* Adjust pointers */  
  240.         newpos+=ctrl[1];  
  241.         oldpos+=ctrl[2];  
  242.     };  
  243.   
  244.     /* Clean up the bzip2 reads */  
  245.     BZ2_bzReadClose(&cbz2err, cpfbz2);  
  246.     BZ2_bzReadClose(&dbz2err, dpfbz2);  
  247.     BZ2_bzReadClose(&ebz2err, epfbz2);  
  248.     if (fclose(cpf) || fclose(dpf) || fclose(epf)) {  
  249.         LOGI("%s", argv[3]);  
  250.         return 1;  
  251.     }  
  252.   
  253.     /* Write the new file */  
  254.     if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||  
  255.         (write(fd,new,newsize)!=newsize) || (close(fd)==-1)) {  
  256.         LOGI("can't open newfile: %s", argv[2]);  
  257.         return 1;  
  258.     }  
  259.   
  260.     free(new);  
  261.     free(old);  
  262.   
  263.     return 0;  
  264. }  
  265.   
  266. JNIEXPORT jint JNICALL Java_com_ywl5320_bspatchywl5320_BsPatchYwl5320Util_bsPatch(JNIEnv *env, jobject instance, jstring oldfile_,  
  267.                                                    jstring newfile_, jstring patchfile_) {  
  268.   
  269.     int restlt = -1;  
  270.     int argc = 4;  
  271.     char *argv[4];  
  272.   
  273.     const char *oldfile = (*env)->GetStringUTFChars(env, oldfile_, 0);  
  274.     const char *newfile = (*env)->GetStringUTFChars(env, newfile_, 0);  
  275.     const char *patchfile = (*env)->GetStringUTFChars(env, patchfile_, 0);  
  276.   
  277.     argv[0] = "bspatch_ywl5320";  
  278.     argv[1] = oldfile;  
  279.     argv[2] = newfile;  
  280.     argv[3] = patchfile;  
  281.     // TODO  
  282.     restlt = patch_main(argc, argv);  
  283.   
  284.     (*env)->ReleaseStringUTFChars(env, oldfile_, oldfile);  
  285.     (*env)->ReleaseStringUTFChars(env, newfile_, newfile);  
  286.     (*env)->ReleaseStringUTFChars(env, patchfile_, patchfile);  
  287.   
  288.     return restlt;  
  289. }  
/*-
 * Copyright 2003-2005 Colin Percival
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions 
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#if 0
__FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $");
#endif

#include "com_ywl5320_bspatchywl5320_BsPatchYwl5320Util.h"
#include "bzip2/bzlib.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
//#include <err.h>
#include <unistd.h>
#include <fcntl.h>

//打印日志
#include <android/log.h>
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"ywl5320",FORMAT,##__VA_ARGS__);

static off_t offtin(u_char *buf)
{
	off_t y;

	y=buf[7]&0x7F;
	y=y*256;y+=buf[6];
	y=y*256;y+=buf[5];
	y=y*256;y+=buf[4];
	y=y*256;y+=buf[3];
	y=y*256;y+=buf[2];
	y=y*256;y+=buf[1];
	y=y*256;y+=buf[0];

	if(buf[7]&0x80) y=-y;

	return y;
}

int patch_main(int argc,char * argv[])
{
	FILE * f, * cpf, * dpf, * epf;
	BZFILE * cpfbz2, * dpfbz2, * epfbz2;
	int cbz2err, dbz2err, ebz2err;
	int fd;
	ssize_t oldsize,newsize;
	ssize_t bzctrllen,bzdatalen;
	u_char header[32],buf[8];
	u_char *old, *new;
	off_t oldpos,newpos;
	off_t ctrl[3];
	off_t lenread;
	off_t i;

	if(argc!=4)
    {
        LOGI("usage: %s oldfile newfile patchfile\n",argv[0])
        return 1;
    }
//        errx(1,);

	/* Open patch file */
	if ((f = fopen(argv[3], "r")) == NULL)
    {
        LOGI("can't find patch file: %s", argv[3]);
        return 1;
    }


	/*
	File format:
		0	8	"BSDIFF40"
		8	8	X
		16	8	Y
		24	8	sizeof(newfile)
		32	X	bzip2(control block)
		32+X	Y	bzip2(diff block)
		32+X+Y	???	bzip2(extra block)
	with control block a set of triples (x,y,z) meaning "add x bytes
	from oldfile to x bytes from the diff block; copy y bytes from the
	extra block; seek forwards in oldfile by z bytes".
	*/

	/* Read header */
	if (fread(header, 1, 32, f) < 32) {
		if (feof(f))
        {
            LOGI("Corrupt patch");
            return 1;
        }
        LOGI("can't read patchfile header: %s", argv[3]);
        return 1;
	}

	/* Check for appropriate magic */
	if (memcmp(header, "BSDIFF40", 8) != 0) {
        LOGI("Corrupt patch\n");
        return 1;
    }

	/* Read lengths from header */
	bzctrllen=offtin(header+8);
	bzdatalen=offtin(header+16);
	newsize=offtin(header+24);
	if((bzctrllen<0) || (bzdatalen<0) || (newsize<0)) {
        LOGI("Corrupt patch\n");
        return 1;
    }

	/* Close patch file and re-open it via libbzip2 at the right places */
	if (fclose(f)) {
        LOGI("%s", argv[3]);
        return 1;
    }
	if ((cpf = fopen(argv[3], "r")) == NULL) {
        LOGI("%s", argv[3]);
        return 1;
    }
	if (fseeko(cpf, 32, SEEK_SET)) {
        LOGI("%s", argv[3]);
        return 1;
    }
	if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL) {
       LOGI("BZ2_bzReadOpen, bz2err = %d", cbz2err);
        return 1;
    }
	if ((dpf = fopen(argv[3], "r")) == NULL) {
        LOGI("%s", argv[3]);
        return 1;
    }
	if (fseeko(dpf, 32 + bzctrllen, SEEK_SET)) {
        LOGI("%s", argv[3]);
        return 1;
    }
	if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL) {
        LOGI("BZ2_bzReadOpen dbz2err = %d", dbz2err);
        return 1;
    }
	if ((epf = fopen(argv[3], "r")) == NULL) {
        LOGI("%s", argv[3]);
        return 1;
    }
	if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET)) {
        LOGI("%s", argv[3]);
        return 1;
    }
	if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL) {
        LOGI("BZ2_bzReadOpen, bz2err = %d", ebz2err);
        return 1;
    }

	if(((fd=open(argv[1],O_RDONLY,0))<0) ||
		((oldsize=lseek(fd,0,SEEK_END))==-1) ||
		((old=malloc(oldsize+1))==NULL) ||
		(lseek(fd,0,SEEK_SET)!=0) ||
		(read(fd,old,oldsize)!=oldsize) ||
		(close(fd)==-1))
    {
        LOGI("can't find oldfile: %s", argv[1]);
        return 1;
    }
	if((new=malloc(newsize+1))==NULL) {
        LOGI("newsize is NULL");
        return 1;
    }

	oldpos=0;newpos=0;
	while(newpos<newsize) {
		/* Read control data */
		for(i=0;i<=2;i++) {
			lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);
			if ((lenread < 8) || ((cbz2err != BZ_OK) &&
			    (cbz2err != BZ_STREAM_END))) {
                LOGI("Corrupt patch\n");
                return 1;
            }
			ctrl[i]=offtin(buf);
		};

		/* Sanity-check */
		if(newpos+ctrl[0]>newsize) {
            LOGI("Corrupt patch\n");
            return 1;
        }

		/* Read diff string */
		lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);
		if ((lenread < ctrl[0]) ||
		    ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END))) {
            LOGI("Corrupt patch\n");
            return 1;
        }

		/* Add old data to diff string */
		for(i=0;i<ctrl[0];i++)
			if((oldpos+i>=0) && (oldpos+i<oldsize))
				new[newpos+i]+=old[oldpos+i];

		/* Adjust pointers */
		newpos+=ctrl[0];
		oldpos+=ctrl[0];

		/* Sanity-check */
		if(newpos+ctrl[1]>newsize) {
            LOGI("Corrupt patch\n");
            return 1;
        }

		/* Read extra string */
		lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);
		if ((lenread < ctrl[1]) ||
		    ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END))) {
            LOGI("Corrupt patch\n");
            return 1;
        }

		/* Adjust pointers */
		newpos+=ctrl[1];
		oldpos+=ctrl[2];
	};

	/* Clean up the bzip2 reads */
	BZ2_bzReadClose(&cbz2err, cpfbz2);
	BZ2_bzReadClose(&dbz2err, dpfbz2);
	BZ2_bzReadClose(&ebz2err, epfbz2);
	if (fclose(cpf) || fclose(dpf) || fclose(epf)) {
        LOGI("%s", argv[3]);
        return 1;
    }

	/* Write the new file */
	if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||
		(write(fd,new,newsize)!=newsize) || (close(fd)==-1)) {
        LOGI("can't open newfile: %s", argv[2]);
        return 1;
    }

	free(new);
	free(old);

	return 0;
}

JNIEXPORT jint JNICALL Java_com_ywl5320_bspatchywl5320_BsPatchYwl5320Util_bsPatch(JNIEnv *env, jobject instance, jstring oldfile_,
                                                   jstring newfile_, jstring patchfile_) {

    int restlt = -1;
    int argc = 4;
    char *argv[4];

    const char *oldfile = (*env)->GetStringUTFChars(env, oldfile_, 0);
    const char *newfile = (*env)->GetStringUTFChars(env, newfile_, 0);
    const char *patchfile = (*env)->GetStringUTFChars(env, patchfile_, 0);

    argv[0] = "bspatch_ywl5320";
    argv[1] = oldfile;
    argv[2] = newfile;
    argv[3] = patchfile;
    // TODO
    restlt = patch_main(argc, argv);

    (*env)->ReleaseStringUTFChars(env, oldfile_, oldfile);
    (*env)->ReleaseStringUTFChars(env, newfile_, newfile);
    (*env)->ReleaseStringUTFChars(env, patchfile_, patchfile);

    return restlt;
}

四、测试差分包合并功能

4.1、准备差分包生成的app_patch1.patch包和就app_old1.apk文件,并在手机根目录下创建bspatch文件夹,添加旧文件和差分包到bspatch中:


4.2、在Mainactivity中添加合并代码

  1. package com.ywl5320.bspatchywl5320;  
  2.   
  3. import android.os.Environment;  
  4. import android.support.v7.app.AppCompatActivity;  
  5. import android.os.Bundle;  
  6. import android.util.Log;  
  7. import android.view.View;  
  8. import android.widget.Toast;  
  9.   
  10.   
  11. public class MainActivity extends AppCompatActivity {  
  12.   
  13.     @Override  
  14.     protected void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         setContentView(R.layout.activity_main);  
  17.     }  
  18.   
  19.   
  20.     public void onPatch(View view) {  
  21.   
  22.         String oldfile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bspatch/app_old1.apk";  
  23.         String newfile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bspatch/app_new1.apk";  
  24.         String patchfile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bspatch/app_patch1.patch";  
  25.   
  26.         int restlt = BsPatchYwl5320Util.getInstance().bsPatch(oldfile, newfile, patchfile);  
  27.         if(restlt == 0)  
  28.         {  
  29.             Log.d("ywl5320""合并成功");  
  30.             Toast.makeText(this"合并成功", Toast.LENGTH_LONG).show();  
  31.         }  
  32.         else  
  33.         {  
  34.             Log.d("ywl5320""合并失败");  
  35.             Toast.makeText(this"合并失败", Toast.LENGTH_LONG).show();  
  36.         }  
  37.     }  
  38. }  
package com.ywl5320.bspatchywl5320;

import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }


    public void onPatch(View view) {

        String oldfile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bspatch/app_old1.apk";
        String newfile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bspatch/app_new1.apk";
        String patchfile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bspatch/app_patch1.patch";

        int restlt = BsPatchYwl5320Util.getInstance().bsPatch(oldfile, newfile, patchfile);
        if(restlt == 0)
        {
            Log.d("ywl5320", "合并成功");
            Toast.makeText(this, "合并成功", Toast.LENGTH_LONG).show();
        }
        else
        {
            Log.d("ywl5320", "合并失败");
            Toast.makeText(this, "合并失败", Toast.LENGTH_LONG).show();
        }
    }
}

4.3、测试合并

最终合并成功,并且可以成功安装。


五、打包.jar文件和提取.so库,方便使用

5.1、打包jar包

  1. jar cvf BsPatchYwl5320Util.jar com\ywl5320\bspatchywl5320\BsPatchYwl5320Util.class  
jar cvf BsPatchYwl5320Util.jar com\ywl5320\bspatchywl5320\BsPatchYwl5320Util.class


5.2、提取Android全平台.so库


六、总结

至此,增量更新服务端生成差分包、客户端合并差分包都已经完成了。实际开发中可以直接使用差分和合并中生成的jar文件和动态库(dll、so),在客户端请求服务器获取差分包时最好验证旧apk的MD5是否一致,因为Android应用一般都是多渠道的,不然容易出错。还有这里的合并只是测试了功能,具体项目中,是获取本apk的系统目录data中的自己的apk为旧文件,然后再和从服务器下载下来的patch文件合并,并重新安装。

具体实现例子,我会在我的另一个开源项目 AppServiceRestFul 中实现。


源码下载: GitHub BsDiffYwl5320