Read http://blog.csdn.net/hmg25/article/details/8100896 before reading this article
What is incremental upgrade? To put it simply, when the application version is updated, the usual practice is to re-download the new version to overwrite the old version, but this has a relatively obvious disadvantage, which is a waste of traffic, especially in GPRS mode. Can we only update the added content of the new version? bsdiff/bzlib2 can help us achieve this. The following describes the specific practices.
1. Generate the difference patch file of the old version and the new version. You can use the Windows version of The bsdiff open-source library.
bsdiff.exe ../iReader1.6.2.0(v35).apk ../iReader1.8.0.1(v40).apk ../ireader.patch
Second, with the patch file, we can use JNI to call bzlib2 on the Android platform to implement incremental upgrades.
1, first of all to have ndk compiling environment, detailed how to build see: http://blog.csdn.net/tibib/article/details/8504680
2. Compile local methods
// Oldapk_filepath: old storage path newapk_savepath: Path of the generated new version to be stored patchpath: difference ratio file storage path public native int applypatchtooldapk (string oldapk_filepath, string newapk_savepath, string patchpath );
3. Compile the Android. mk configuration file and copy the bzlib2 source code file () to the directory.
Local_path: = $ (call my-DIR) include $ (clear_vars) # This is the target being built. local_module: = libbsdiff # all of the source files that we will compile. # The specific C code is required. I have not carefully studied local_src_files: = tu_bingbing_bsdiff_bsdiffbusiness.c \ bzlib. c \ blocksort. c \ compress. c \ crctable. c \ decompress. c \ Huffman. c \ randtable. c \ bzip2.c \ # No static libraries. local_static_libraries: = \ libbz # also need the JNI headers. local_c_includes + =\$ (jni_h_include) External/Bzip2 # No special compiler flags. local_cflags + = include $ (build_shared_library)
4. Implement local methods
# Include <stdio. h> # include "tu_bingbing_bsdiff_bsdiffbusiness.h" # include "bzlib_private.h" # include <stdlib. h> # include <stdio. h> # include <string. h> # include <err. h> # include <unistd. h> # include <fcntl. h> # include <Android/log. h> 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 * 25 6; y + = Buf [0]; If (BUF [7] & 0x80) y =-y; return y;} int applypatch (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) errx (1, "Usage: % s oldfile newfile patchfile \ n", argv [0]); /* open patch file */If (F = fopen (argv [3], "R") = NULL) Err (1, "fopen (% s )", argv [3]);/* File Format: 0 8 "bsdiff40" 8 8x16 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) errx (1, "updated upt patch \ n"); err (1, "fread (% s)", argv [3]);} /* Check for appropriate magic */If (memcmp (header, "bsdiff40", 8)! = 0) errx (1, "Upload upt patch \ n");/* read lengths From header */bzctrllen = offtin (header + 8 ); bzdatalen = offtin (header + 16); newsize = offtin (header + 24); If (bzctrllen <0) | (bzdatalen <0) | (newsize <0 )) errx (1, "Upload upt patch \ n");/* close patch file and re-open it via libbzip2 at the right places */If (fclose (f )) err (1, "fclose (% s)", argv [3]); If (CPF = fopen (argv [3], "R") = NULL) err (1, "fopen (% s )", Argv [3]); If (fseeko (CPF, 32, seek_set) Err (1, "fseeko (% s, % LLD)", argv [3], (long) 32); If (cpfbz2 = bz2_bzreadopen (& cbz2err, CPF, 0, 0, null, 0) = NULL) errx (1, "bz2_bzreadopen, bz2err = % d ", cbz2err); If (DPF = fopen (argv [3]," R ") = NULL) Err (1," fopen (% s) ", argv [3]); If (fseeko (DPF, 32 + bzctrllen, seek_set) Err (1," fseeko (% s, % LLD )", argv [3], (long) (32 + bzctrllen); If (DP Fbz2 = bz2_bzreadopen (& dbz2err, DPF, 0, 0, null, 0) = NULL) errx (1, "bz2_bzreadopen, bz2err = % d", dbz2err ); if (EPF = fopen (argv [3], "R") = NULL) Err (1, "fopen (% s)", argv [3]); if (fseeko (EPF, 32 + bzctrllen + bzdatalen, seek_set) Err (1, "fseeko (% s, % LLD)", argv [3], (long) (32 + bzctrllen + bzdatalen); If (epfbz2 = bz2_bzreadopen (& ebz2err, EPF, 0, 0, null, 0) = NULL) errx (1, "bz2_bzr Eadopen, bz2err = % d ", ebz2err); 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) Err (1, "% s", argv [1]); if (New = malloc (newsize + 1) = NULL) Err (1, null); 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) errx (1, "Upload upt patch \ n"); CTRL [I] = offtin (BUF );}; /* sanity-check */If (newpos + ctrl [0]> newsize) errx (1, "failed upt patch \ n "); /* read diff string */lenread = bz2_bzread (& dbz2err, dpfbz2, new + newpos, CTRL [0]); If (lenread <Ctrl [0]) | (dbz2err! = Bz_ OK) & (dbz2err! = Bz_stream_end) errx (1, "Upload upt patch \ n");/* 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) errx (1, "Upload upt patch \ n");/* read extra string */lenread = bz2_bzread (& ebz2err, epfbz2, new + newpos, CTRL [1]); if (lenrea D <Ctrl [1]) | (ebz2err! = Bz_ OK) & (ebz2err! = Bz_stream_end) errx (1, "Upload upt patch \ n");/* adjust pointers */newpos + = CTRL [1]; oldpos + = CTRL [2];};/* clean up the Bzip2 reads */lower (& cbz2err, cpfbz2); bz2_bzreadclose (& dbz2err, dpfbz2); lower (& ebz2err, epfbz2); If (fclose (CPF) | fclose (DPF) | fclose (EPF) Err (1, "fclose (% s)", argv [3]); /* 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) Err (1, "% s", argv [2]); free (new ); free (old); Return 0;} // path of the APK package before the old upgrade // path of the APK package after the new upgrade // path of the patch file, you can use bsdiff tool to generate // The specific principle can see the http://blog.csdn.net/hmg25/article/details/8100896JNIEXPORT jint jnicall partition (jnienv * ENV, jobject OBJ, jstring old, jstring new, jstring patch) {int argc = 4; char * argv [argc]; argv [0] = "bspatch"; argv [1] = (char *) (* env)-> getstringutfchars (ENV, old, 0); argv [2] = (char *) (* env)-> getstringutfchars (ENV, new, 0); argv [3] = (char *) (* env)-> getstringutfchars (ENV, patch, 0); int ret = applypatch (argc, argv); (* env)-> releasestringutfchars (ENV, old, argv [1]); (* env)-> releasestringutfchars (ENV, new, argv [2]); (* env)-> releasestringutfchars (ENV, patch, argv [3]); return ret ;}
Finally, the ndk is compiled and the native method is called in Android. You will find that a new version of APK is generated under your input path.
Demo: http://download.csdn.net/detail/tibib/5581905