Android应用增量升级
阅读原文时间:2021年04月20日阅读:1

[

](http://blog.csdn.net/tibib/article/details/8538592)

阅读此文之前请先阅读  http://blog.csdn.net/hmg25/article/details/8100896

何为增量升级,简单说下,当应用版本要更新时通常的做法是重新下载新的版本去覆盖旧版本,但这样有个比较明显缺点,太浪费流量了,尤其是在GPRS模式下。我们能不能只更新新版本增加的内容呢,bsdiff/bzlib2可以帮助我们实现这点。下面介绍下具体的做法

第一、生成旧版和新版的差分比patch文件,可以借助bsdiff开源库windows版本

[html]  view plain copy

  1. bsdiff.exe   ../iReader1.6.2.0(v35).apk   ../iReader1.8.0.1(v40).apk   ../ireader.patch

第二、有了patch文件,我们就可以在Android平台上利用JNI调用bzlib2就可以实现增量升级了。

1、首先要有ndk编译环境,具体怎么搭建详见:http://blog.csdn.net/tibib/article/details/8504680

2、编写本地方法

[java]  view plain copy

  1. //oldapk_filepath:旧版本存储路径   newapk_savepath:生成的新版本要存放的路径  patchpath:差分比文件存放路径
  2. public native int applyPatchToOldApk(String oldapk_filepath, String newapk_savepath , String patchpath);

3、编写Android.mk配置文件,并把需要的bzlib2源代码文件()拷贝到目录下

[html]  view plain copy

  1. LOCAL_PATH:= $(call my-dir)

  2. include $(CLEAR_VARS)

  3. # This is the target being built.

  4. LOCAL_MODULE:= libBsdiff

  5. # All of the source files that we will compile.

  6. # 具体到底需要哪些c代码,没有仔细研究过

  7. LOCAL_SRC_FILES:= tu_bingbing_bsdiff_BsdiffBusiness.c \

  8. bzlib.c \

  9. blocksort.c \

  10. compress.c \

  11. crctable.c \

  12. decompress.c \

  13. huffman.c \

  14. randtable.c \

  15. bzip2.c \

  16. # No static libraries.

  17. LOCAL_STATIC_LIBRARIES := \

  18. libbz

  19. # Also need the JNI headers.

  20. LOCAL_C_INCLUDES += \

  21. $(JNI_H_INCLUDE) external/bzip2

  22. # No special compiler flags.

  23. LOCAL_CFLAGS +=

  24. include $(BUILD_SHARED_LIBRARY)

4、实现本地方法

[cpp]  view plain copy

  1. #include 

  2. #include "tu_bingbing_bsdiff_BsdiffBusiness.h"

  3. #include "bzlib_private.h"

  4. #include 

  5. #include 

  6. #include 

  7. #include 

  8. #include 

  9. #include 

  10. #include 

  11. static off_t offtin(u_char *buf)

  12. {

  13. off_t y;

  14. y=buf[7]&0x7F;

  15. y=y*256;y+=buf[6];

  16. y=y*256;y+=buf[5];

  17. y=y*256;y+=buf[4];

  18. y=y*256;y+=buf[3];

  19. y=y*256;y+=buf[2];

  20. y=y*256;y+=buf[1];

  21. y=y*256;y+=buf[0];

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

  23. return y;

  24. }

  25. int applypatch(int argc,char * argv[])

  26. {

  27. FILE * f, * cpf, * dpf, * epf;

  28. BZFILE * cpfbz2, * dpfbz2, * epfbz2;

  29. int cbz2err, dbz2err, ebz2err;

  30. int fd;

  31. ssize_t oldsize,newsize;

  32. ssize_t bzctrllen,bzdatalen;

  33. u_char header[32],buf[8];

  34. u_char *old, *new;

  35. off_t oldpos,newpos;

  36. off_t ctrl[3];

  37. off_t lenread;

  38. off_t i;

  39. if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);

  40. /* Open patch file */

  41. if ((f = fopen(argv[3], "r")) == NULL)

  42. err(1, "fopen(%s)", argv[3]);

  43. /*

  44. File format:

  45. 0   8   "BSDIFF40"

  46. 8   8   X

  47. 16  8   Y

  48. 24  8   sizeof(newfile)

  49. 32  X   bzip2(control block)

  50. 32+X    Y   bzip2(diff block)

  51. 32+X+Y  ??? bzip2(extra block)

  52. with control block a set of triples (x,y,z) meaning "add x bytes

  53. from oldfile to x bytes from the diff block; copy y bytes from the

  54. extra block; seek forwards in oldfile by z bytes".

  55. */

  56. /* Read header */

  57. if (fread(header, 1, 32, f) < 32) {

  58. if (feof(f))

  59. errx(1, "Corrupt patch\n");

  60. err(1, "fread(%s)", argv[3]);

  61. }

  62. /* Check for appropriate magic */

  63. if (memcmp(header, "BSDIFF40", 8) != 0)

  64. errx(1, "Corrupt patch\n");

  65. /* Read lengths from header */

  66. bzctrllen=offtin(header+8);

  67. bzdatalen=offtin(header+16);

  68. newsize=offtin(header+24);

  69. if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))

  70. errx(1,"Corrupt patch\n");

  71. /* Close patch file and re-open it via libbzip2 at the right places */

  72. if (fclose(f))

  73. err(1, "fclose(%s)", argv[3]);

  74. if ((cpf = fopen(argv[3], "r")) == NULL)

  75. err(1, "fopen(%s)", argv[3]);

  76. if (fseeko(cpf, 32, SEEK_SET))

  77. err(1, "fseeko(%s, %lld)", argv[3],

  78. (long long)32);

  79. if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)

  80. errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);

  81. if ((dpf = fopen(argv[3], "r")) == NULL)

  82. err(1, "fopen(%s)", argv[3]);

  83. if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))

  84. err(1, "fseeko(%s, %lld)", argv[3],

  85. (long long)(32 + bzctrllen));

  86. if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)

  87. errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);

  88. if ((epf = fopen(argv[3], "r")) == NULL)

  89. err(1, "fopen(%s)", argv[3]);

  90. if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))

  91. err(1, "fseeko(%s, %lld)", argv[3],

  92. (long long)(32 + bzctrllen + bzdatalen));

  93. if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)

  94. errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);

  95. if(((fd=open(argv[1],O_RDONLY,0))<0) ||

  96. ((oldsize=lseek(fd,0,SEEK_END))==-1) ||

  97. ((old=malloc(oldsize+1))==NULL) ||

  98. (lseek(fd,0,SEEK_SET)!=0) ||

  99. (read(fd,old,oldsize)!=oldsize) ||

  100. (close(fd)==-1)) err(1,"%s",argv[1]);

  101. if((new=malloc(newsize+1))==NULL) err(1,NULL);

  102. oldpos=0;newpos=0;

  103. while(newpos<newsize) {

  104. /* Read control data */

  105. for(i=0;i<=2;i++) {

  106. lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);

  107. if ((lenread < 8) || ((cbz2err != BZ_OK) &&

  108. (cbz2err != BZ_STREAM_END)))

  109. errx(1, "Corrupt patch\n");

  110. ctrl[i]=offtin(buf);

  111. };

  112. /* Sanity-check */

  113. if(newpos+ctrl[0]>newsize)

  114. errx(1,"Corrupt patch\n");

  115. /* Read diff string */

  116. lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);

  117. if ((lenread < ctrl[0]) ||

  118. ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))

  119. errx(1, "Corrupt patch\n");

  120. /* Add old data to diff string */

  121. for(i=0;i<ctrl[0];i++)

  122. if((oldpos+i>=0) && (oldpos+i<oldsize))

  123. new[newpos+i]+=old[oldpos+i];

  124. /* Adjust pointers */

  125. newpos+=ctrl[0];

  126. oldpos+=ctrl[0];

  127. /* Sanity-check */

  128. if(newpos+ctrl[1]>newsize)

  129. errx(1,"Corrupt patch\n");

  130. /* Read extra string */

  131. lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);

  132. if ((lenread < ctrl[1]) ||

  133. ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))

  134. errx(1, "Corrupt patch\n");

  135. /* Adjust pointers */

  136. newpos+=ctrl[1];

  137. oldpos+=ctrl[2];

  138. };

  139. /* Clean up the bzip2 reads */

  140. BZ2_bzReadClose(&cbz2err, cpfbz2);

  141. BZ2_bzReadClose(&dbz2err, dpfbz2);

  142. BZ2_bzReadClose(&ebz2err, epfbz2);

  143. if (fclose(cpf) || fclose(dpf) || fclose(epf))

  144. err(1, "fclose(%s)", argv[3]);

  145. /* Write the new file */

  146. if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||

  147. (write(fd,new,newsize)!=newsize) || (close(fd)==-1))

  148. err(1,"%s",argv[2]);

  149. free(new);

  150. free(old);

  151. return 0;

  152. }

  153. // old 升级之前apk包路径

  154. // new 升级之后apk包路径

  155. // patch文件,可以用bsdiff工具生成

  156. // 具体原理可以参看http://blog.csdn.net/hmg25/article/details/8100896

  157. JNIEXPORT jint JNICALL Java_tu_bingbing_bsdiff_BsdiffBusiness_applyPatchToOldApk(JNIEnv *env,

  158. jobject obj, jstring old, jstring new , jstring patch){

  159. int argc=4;

  160. char * argv[argc];

  161. argv[0]="bspatch";

  162. argv[1]=(char*)((*env)->GetStringUTFChars(env,old, 0));

  163. argv[2]=(char*)((*env)->GetStringUTFChars(env,new, 0));

  164. argv[3]=(char*)((*env)->GetStringUTFChars(env,patch, 0));

  165. int ret=applypatch(argc, argv);

  166. (*env)->ReleaseStringUTFChars(env,old,argv[1]);

  167. (*env)->ReleaseStringUTFChars(env,new,argv[2]);

  168. (*env)->ReleaseStringUTFChars(env,patch,argv[3]);

  169. return ret;

  170. }

最后 ndk编译,在Android中调用native方法,你会发现在你传入路径下生成了新版本的apk。

DEMO:http://download.csdn.net/detail/tibib/5581905

Android应用增量升级

阅读此文之前请先阅读  http://blog.csdn.net/hmg25/article/details/8100896

何为增量升级,简单说下,当应用版本要更新时通常的做法是重新下载新的版本去覆盖旧版本,但这样有个比较明显缺点,太浪费流量了,尤其是在GPRS模式下。我们能不能只更新新版本增加的内容呢,bsdiff/bzlib2可以帮助我们实现这点。下面介绍下具体的做法

第一、生成旧版和新版的差分比patch文件,可以借助bsdiff开源库windows版本

[html]  view plain copy

  1. bsdiff.exe   ../iReader1.6.2.0(v35).apk   ../iReader1.8.0.1(v40).apk   ../ireader.patch

第二、有了patch文件,我们就可以在Android平台上利用JNI调用bzlib2就可以实现增量升级了。

1、首先要有ndk编译环境,具体怎么搭建详见:http://blog.csdn.net/tibib/article/details/8504680

2、编写本地方法

[java]  view plain copy

  1. //oldapk_filepath:旧版本存储路径   newapk_savepath:生成的新版本要存放的路径  patchpath:差分比文件存放路径
  2. public native int applyPatchToOldApk(String oldapk_filepath, String newapk_savepath , String patchpath);

3、编写Android.mk配置文件,并把需要的bzlib2源代码文件()拷贝到目录下

[html]  view plain copy

  1. LOCAL_PATH:= $(call my-dir)

  2. include $(CLEAR_VARS)

  3. # This is the target being built.

  4. LOCAL_MODULE:= libBsdiff

  5. # All of the source files that we will compile.

  6. # 具体到底需要哪些c代码,没有仔细研究过

  7. LOCAL_SRC_FILES:= tu_bingbing_bsdiff_BsdiffBusiness.c \

  8. bzlib.c \

  9. blocksort.c \

  10. compress.c \

  11. crctable.c \

  12. decompress.c \

  13. huffman.c \

  14. randtable.c \

  15. bzip2.c \

  16. # No static libraries.

  17. LOCAL_STATIC_LIBRARIES := \

  18. libbz

  19. # Also need the JNI headers.

  20. LOCAL_C_INCLUDES += \

  21. $(JNI_H_INCLUDE) external/bzip2

  22. # No special compiler flags.

  23. LOCAL_CFLAGS +=

  24. include $(BUILD_SHARED_LIBRARY)

4、实现本地方法

[cpp]  view plain copy

  1. #include 

  2. #include "tu_bingbing_bsdiff_BsdiffBusiness.h"

  3. #include "bzlib_private.h"

  4. #include 

  5. #include 

  6. #include 

  7. #include 

  8. #include 

  9. #include 

  10. #include 

  11. static off_t offtin(u_char *buf)

  12. {

  13. off_t y;

  14. y=buf[7]&0x7F;

  15. y=y*256;y+=buf[6];

  16. y=y*256;y+=buf[5];

  17. y=y*256;y+=buf[4];

  18. y=y*256;y+=buf[3];

  19. y=y*256;y+=buf[2];

  20. y=y*256;y+=buf[1];

  21. y=y*256;y+=buf[0];

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

  23. return y;

  24. }

  25. int applypatch(int argc,char * argv[])

  26. {

  27. FILE * f, * cpf, * dpf, * epf;

  28. BZFILE * cpfbz2, * dpfbz2, * epfbz2;

  29. int cbz2err, dbz2err, ebz2err;

  30. int fd;

  31. ssize_t oldsize,newsize;

  32. ssize_t bzctrllen,bzdatalen;

  33. u_char header[32],buf[8];

  34. u_char *old, *new;

  35. off_t oldpos,newpos;

  36. off_t ctrl[3];

  37. off_t lenread;

  38. off_t i;

  39. if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);

  40. /* Open patch file */

  41. if ((f = fopen(argv[3], "r")) == NULL)

  42. err(1, "fopen(%s)", argv[3]);

  43. /*

  44. File format:

  45. 0   8   "BSDIFF40"

  46. 8   8   X

  47. 16  8   Y

  48. 24  8   sizeof(newfile)

  49. 32  X   bzip2(control block)

  50. 32+X    Y   bzip2(diff block)

  51. 32+X+Y  ??? bzip2(extra block)

  52. with control block a set of triples (x,y,z) meaning "add x bytes

  53. from oldfile to x bytes from the diff block; copy y bytes from the

  54. extra block; seek forwards in oldfile by z bytes".

  55. */

  56. /* Read header */

  57. if (fread(header, 1, 32, f) < 32) {

  58. if (feof(f))

  59. errx(1, "Corrupt patch\n");

  60. err(1, "fread(%s)", argv[3]);

  61. }

  62. /* Check for appropriate magic */

  63. if (memcmp(header, "BSDIFF40", 8) != 0)

  64. errx(1, "Corrupt patch\n");

  65. /* Read lengths from header */

  66. bzctrllen=offtin(header+8);

  67. bzdatalen=offtin(header+16);

  68. newsize=offtin(header+24);

  69. if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))

  70. errx(1,"Corrupt patch\n");

  71. /* Close patch file and re-open it via libbzip2 at the right places */

  72. if (fclose(f))

  73. err(1, "fclose(%s)", argv[3]);

  74. if ((cpf = fopen(argv[3], "r")) == NULL)

  75. err(1, "fopen(%s)", argv[3]);

  76. if (fseeko(cpf, 32, SEEK_SET))

  77. err(1, "fseeko(%s, %lld)", argv[3],

  78. (long long)32);

  79. if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)

  80. errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);

  81. if ((dpf = fopen(argv[3], "r")) == NULL)

  82. err(1, "fopen(%s)", argv[3]);

  83. if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))

  84. err(1, "fseeko(%s, %lld)", argv[3],

  85. (long long)(32 + bzctrllen));

  86. if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)

  87. errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);

  88. if ((epf = fopen(argv[3], "r")) == NULL)

  89. err(1, "fopen(%s)", argv[3]);

  90. if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))

  91. err(1, "fseeko(%s, %lld)", argv[3],

  92. (long long)(32 + bzctrllen + bzdatalen));

  93. if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)

  94. errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);

  95. if(((fd=open(argv[1],O_RDONLY,0))<0) ||

  96. ((oldsize=lseek(fd,0,SEEK_END))==-1) ||

  97. ((old=malloc(oldsize+1))==NULL) ||

  98. (lseek(fd,0,SEEK_SET)!=0) ||

  99. (read(fd,old,oldsize)!=oldsize) ||

  100. (close(fd)==-1)) err(1,"%s",argv[1]);

  101. if((new=malloc(newsize+1))==NULL) err(1,NULL);

  102. oldpos=0;newpos=0;

  103. while(newpos<newsize) {

  104. /* Read control data */

  105. for(i=0;i<=2;i++) {

  106. lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);

  107. if ((lenread < 8) || ((cbz2err != BZ_OK) &&

  108. (cbz2err != BZ_STREAM_END)))

  109. errx(1, "Corrupt patch\n");

  110. ctrl[i]=offtin(buf);

  111. };

  112. /* Sanity-check */

  113. if(newpos+ctrl[0]>newsize)

  114. errx(1,"Corrupt patch\n");

  115. /* Read diff string */

  116. lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);

  117. if ((lenread < ctrl[0]) ||

  118. ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))

  119. errx(1, "Corrupt patch\n");

  120. /* Add old data to diff string */

  121. for(i=0;i<ctrl[0];i++)

  122. if((oldpos+i>=0) && (oldpos+i<oldsize))

  123. new[newpos+i]+=old[oldpos+i];

  124. /* Adjust pointers */

  125. newpos+=ctrl[0];

  126. oldpos+=ctrl[0];

  127. /* Sanity-check */

  128. if(newpos+ctrl[1]>newsize)

  129. errx(1,"Corrupt patch\n");

  130. /* Read extra string */

  131. lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);

  132. if ((lenread < ctrl[1]) ||

  133. ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))

  134. errx(1, "Corrupt patch\n");

  135. /* Adjust pointers */

  136. newpos+=ctrl[1];

  137. oldpos+=ctrl[2];

  138. };

  139. /* Clean up the bzip2 reads */

  140. BZ2_bzReadClose(&cbz2err, cpfbz2);

  141. BZ2_bzReadClose(&dbz2err, dpfbz2);

  142. BZ2_bzReadClose(&ebz2err, epfbz2);

  143. if (fclose(cpf) || fclose(dpf) || fclose(epf))

  144. err(1, "fclose(%s)", argv[3]);

  145. /* Write the new file */

  146. if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||

  147. (write(fd,new,newsize)!=newsize) || (close(fd)==-1))

  148. err(1,"%s",argv[2]);

  149. free(new);

  150. free(old);

  151. return 0;

  152. }

  153. // old 升级之前apk包路径

  154. // new 升级之后apk包路径

  155. // patch文件,可以用bsdiff工具生成

  156. // 具体原理可以参看http://blog.csdn.net/hmg25/article/details/8100896

  157. JNIEXPORT jint JNICALL Java_tu_bingbing_bsdiff_BsdiffBusiness_applyPatchToOldApk(JNIEnv *env,

  158. jobject obj, jstring old, jstring new , jstring patch){

  159. int argc=4;

  160. char * argv[argc];

  161. argv[0]="bspatch";

  162. argv[1]=(char*)((*env)->GetStringUTFChars(env,old, 0));

  163. argv[2]=(char*)((*env)->GetStringUTFChars(env,new, 0));

  164. argv[3]=(char*)((*env)->GetStringUTFChars(env,patch, 0));

  165. int ret=applypatch(argc, argv);

  166. (*env)->ReleaseStringUTFChars(env,old,argv[1]);

  167. (*env)->ReleaseStringUTFChars(env,new,argv[2]);

  168. (*env)->ReleaseStringUTFChars(env,patch,argv[3]);

  169. return ret;

  170. }

最后 ndk编译,在Android中调用native方法,你会发现在你传入路径下生成了新版本的apk。

DEMO:http://download.csdn.net/detail/tibib/5581905

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章