您的位置:首页 > 移动开发 > Android开发

Android应用增量升级

2014-11-20 18:35 525 查看
转载自:/article/1648995.html

阅读此文之前请先阅读 /article/2759349.html

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

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

[html] view
plaincopy

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
plaincopy

//oldapk_filepath:旧版本存储路径 newapk_savepath:生成的新版本要存放的路径 patchpath:差分比文件存放路径

public native int applyPatchToOldApk(String oldapk_filepath, String newapk_savepath , String patchpath);

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

[html] view
plaincopy

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.

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

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、实现本地方法

[cpp] view
plaincopy

#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*256;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 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))

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

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

}

/* Check for appropriate magic */

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

errx(1, "Corrupt 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,"Corrupt 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 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 long)(32 + bzctrllen));

if ((dpfbz2 = 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 long)(32 + bzctrllen + bzdatalen));

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

errx(1, "BZ2_bzReadOpen, 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, "Corrupt patch\n");

ctrl[i]=offtin(buf);

};

/* Sanity-check */

if(newpos+ctrl[0]>newsize)

errx(1,"Corrupt 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, "Corrupt 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,"Corrupt patch\n");

/* Read extra string */

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

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

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

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

/* 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))

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;

}

// old 升级之前apk包路径

// new 升级之后apk包路径

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

// 具体原理可以参看/article/2759349.html

JNIEXPORT jint JNICALL Java_tu_bingbing_bsdiff_BsdiffBusiness_applyPatchToOldApk(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;

}

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

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

阅读此文之前请先阅读 /article/2759349.html

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

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

[html] view
plaincopy

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
plaincopy

//oldapk_filepath:旧版本存储路径 newapk_savepath:生成的新版本要存放的路径 patchpath:差分比文件存放路径

public native int applyPatchToOldApk(String oldapk_filepath, String newapk_savepath , String patchpath);

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

[html] view
plaincopy

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.

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

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、实现本地方法

[cpp] view
plaincopy

#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*256;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 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))

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

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

}

/* Check for appropriate magic */

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

errx(1, "Corrupt 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,"Corrupt 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 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 long)(32 + bzctrllen));

if ((dpfbz2 = 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 long)(32 + bzctrllen + bzdatalen));

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

errx(1, "BZ2_bzReadOpen, 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, "Corrupt patch\n");

ctrl[i]=offtin(buf);

};

/* Sanity-check */

if(newpos+ctrl[0]>newsize)

errx(1,"Corrupt 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, "Corrupt 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,"Corrupt patch\n");

/* Read extra string */

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

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

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

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

/* 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))

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;

}

// old 升级之前apk包路径

// new 升级之后apk包路径

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

// 具体原理可以参看/article/2759349.html

JNIEXPORT jint JNICALL Java_tu_bingbing_bsdiff_BsdiffBusiness_applyPatchToOldApk(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;

}

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

DEMO:http://download.csdn.net/detail/tibib/5581905
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: