浅析android应用增量升级(转)
By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处
很久没有更新博客了,真是堕落啊,几次想提起笔,却总是被各种琐事耽搁,以后会多写文章记录点滴。
背景
增量升级的原理
增量升级的操作
- ./build/tools/releasetools/ota_from_target_files -n -i <旧包> <新包> <差分包名>
./build/tools/releasetools/ota_from_target_files -n -i <旧包> <新包> <差分包名>
- diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
至此我们就看到了android中提供我们用来制作差分增量升级包的工具,"bsdiff",这是一个很牛X开源的二进制差分工具.相关的介绍传送门
- 命令:bsdiff oldfile newfile patchfile
- 例如: bsdiff xx_v1.0.apk xx_v2.0.apk xx.patch
命令:bsdiff oldfile newfile patchfile
例如: bsdiff xx_v1.0.apk xx_v2.0.apk xx.patch
- bspatch的命令格式为:
- bspatch oldfile newfile patchfile
bspatch的命令格式为:
bspatch oldfile newfile patchfile
不足
小实验
- ├── bsdiff-4.3 //bsdiff的源码路径,官网获取
- │ ├── bsdiff.1
- │ ├── bsdiff.c
- │ ├── bspatch.1
- │ ├── bspatch.c
- │ └── Makefile
- ├── bsdiff-4.3.tar.gz
- ├── bsdiff4.3-win32 //windows PC端的测试工具
- │ ├── Binary diff.txt
- │ ├── bsdiff.exe
- │ ├── bspatch.exe
- │ └── LICENSE
- ├── bspatch //手机端的测试工具
- ├── iReader1.6.2.0(v35).apk // 旧版本的apk
- └── iReader1.8.0.1(v40).apk //新版本的apk
├── bsdiff-4.3 //bsdiff的源码路径,官网获取
│ ├── bsdiff.1
│ ├── bsdiff.c
│ ├── bspatch.1
│ ├── bspatch.c
│ └── Makefile
├── bsdiff-4.3.tar.gz
├── bsdiff4.3-win32 //windows PC端的测试工具
│ ├── Binary diff.txt
│ ├── bsdiff.exe
│ ├── bspatch.exe
│ └── LICENSE
├── bspatch //手机端的测试工具
├── iReader1.6.2.0(v35).apk // 旧版本的apk
└── iReader1.8.0.1(v40).apk //新版本的apk
以附带的iReader来做测试,在shell进入test\bsdiff4.3-win32文件夹,并下运行命令:
- bsdiff.exe ../iReader1.6.2.0(v35).apk ../iReader1.8.0.1(v40).apk ../ireader.patch
bsdiff.exe ../iReader1.6.2.0(v35).apk ../iReader1.8.0.1(v40).apk ../ireader.patch
原来的apk(2.94M),新版本的(3.24M),得到的patch文件为1.77M,用户需要下载的就只是1.77M,流量节省了很多。
下面先在电脑端将他们合并。
- bspatch.exe ../iReader1.6.2.0(v35).apk ../new.apk ../ireader.patch
bspatch.exe ../iReader1.6.2.0(v35).apk ../new.apk ../ireader.patch
执行后得到名为new.apk 的合成版本应用,我在ubuntu下进行校验可以看出他们是一样的。
扩展阅读
Full update 10,385,920
大牛们可以去研究下。
最近有些小忙,稍后有时间会对增量升级进行封装下,将合成的代码弄成一个lib库,供java调用。有兴趣的童鞋可以自己操作一下~~~
- #include <stdio.h>
- #include "com_hmg25_newstart_BSpatch.h"
- #include <bzlib.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;
- }
- JNIEXPORT jint JNICALL Java_com_hmg25_newstart_BSpatch_applyPatch(JNIEnv *env,
- jobject obj, jstring old, jstring new , jstring patch){
- int argc=4;
- char * argv[argc];
- argv[0]="bspatch";
- argv[1]=(*env)->GetStringUTFChars(env,old, 0);
- argv[2]=(*env)->GetStringUTFChars(env,new, 0);
- argv[3]=(*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;
- }
#include <stdio.h>
#include "com_hmg25_newstart_BSpatch.h" #include <bzlib.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;
} JNIEXPORT jint JNICALL Java_com_hmg25_newstart_BSpatch_applyPatch(JNIEnv *env,
jobject obj, jstring old, jstring new , jstring patch){
int argc=4;
char * argv[argc];
argv[0]="bspatch";
argv[1]=(*env)->GetStringUTFChars(env,old, 0);
argv[2]=(*env)->GetStringUTFChars(env,new, 0);
argv[3]=(*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;
}
Android.mk:
- LOCAL_PATH:= $(call my-dir)
- include $(CLEAR_VARS)
- # This is the target being built.
- LOCAL_MODULE:= libbspatch
- # All of the source files that we will compile.
- LOCAL_SRC_FILES:= \
- com_hmg25_newstart_BSpatch.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)
浅析android应用增量升级(转)的更多相关文章
- Android App增量升级
移动互联网主打的就是用户体验和产品的快速迭代,通过用户反馈和用户行为跟踪及时调整产品方向,这样才能持续保持生命力和创造力.说的接地气点就是,你频繁的升级更新,有时只是修复了几个bug或者微调了下界面, ...
- Mac平台与Windows平台下AndroidStudio增量升级
Android Studio增量升级什么情况下使用最合适呢? 比如现在的as版本是2.2版本,而你的as版本2.0版本,这个时候点Check For Updates就没有反应了,因为你已经2个有版本没 ...
- 手把手实现andriod应用增量升级
近期研究了android应用增量升级的应用.当中用到了android NDK编程,先说下为什么要使用增量升级.当我们的应用达到一定大小的时候,比方眼下有30M.假设新版本号35M仅仅是添加了几个功能, ...
- 待实验:Android 增量升级
参考资料: 增量升级(省流量更新)的Android客户端实现 http://blog.csdn.net/sgwhp/article/details/9009427 http://my.oschina ...
- Android增量升级的方法和原理
总结: 我们使用delta编码算法减少Android应用升级程序的大小.我们通过bsdiff和bspatch工具在android上实现delta编码算法.服务器软件和android应用已经部署.当前, ...
- android黑科技系列——应用市场省流量更新(增量升级)原理解析
一.前言 最近在看热修复相关的框架,之前我们已经看过了阿里的Dexposed和AndFix这两个框架了,不了解的同学可以点击这里进行查看:Dexposed框架原理解析 和 AndFix热修复框架原理解 ...
- react native 增量升级方案(转)
前言 facebook的react-native给我们带来了用js写出原生应用的同时,也使得使用RN编写的代码的在线升级变得可能,终于可以不通过应用市场来进行升级,极大的提升了app修bug和赋予新功 ...
- Android Recovery OTA升级(一)—— make otapackage
文件夹 文件夹 概述 make otapackage BUILT_TARGET_FILES_PACKAGE ota_from_target_files WriteFullOTAPackage Sign ...
- android开发 更新升级安装到一半自动闪退
如题:android开发 更新升级安装到一半自动闪退,,,解决办法,如下(红色为我新增的代码) /** * 安装APK文件 */ private void installApk( ...
随机推荐
- authentication not supported Connect to TFS Git from Xamarin Studio (non-hosted, locally installed TFS 2013)
There are several instructions on how to connect to TFS Git from Xamarin Studio if you're using the ...
- C++回调函数(callback)的使用
什么是回调函数(callback) 模块A有一个函数foo,他向模块B传递foo的地址,然后在B里面发生某种事件(event)时,通过从A里面传递过来的foo的地址调用foo,通知A发生了什么事 ...
- Android项目实战之(1)-- 开发一个"快速冲浪"的程序
概述:这个小程序,你讲学习到基本控件(Button,Listview,Gridview,TextView等)的使用技巧,AssetManager类的使用,XML数据的解析方式,BaseAdapter, ...
- Selenium2+python自动化59-数据驱动(ddt)
前言 在设计用例的时候,有些用例只是参数数据的输入不一样,比如登录这个功能,操作过程但是一样的.如果用例重复去写操作过程会增加代码量,对应这种多组数据的测试用例,可以用数据驱动设计模式,一组数据对应一 ...
- UIScrollView视差模糊效果
UIScrollView视差模糊效果 效果 源码 https://github.com/YouXianMing/Animations // // ScrollBlurImageViewControll ...
- java执行ping命令
public static void get() throws IOException{ String address="10.132.118.110"; Process proc ...
- 项目bug的修正
这几个月来,大部分业余时间,都花在阅读软件工程和编译原理方面的书籍上了.软件工程方面的书,包括软件需求.风险管理.敏捷建模,系统设计,软件项目管理,还有一些类似于的沉思录书籍等. 在这些书中,都只是讲 ...
- 【BZOJ】【1072】【SCOI2007】排列perm
暴力 ……傻逼题我还WA了这么多次(有几次是忘了删调试信息……sigh) 直接统计0~9各有多少个,枚举数字就行了……因为是直接枚举的数字,而不是枚举用了s中的哪一位,所以是不用去重的!(我一开始写的 ...
- 用java查询HBase中某表的一批数据
java代码如下: package db.query; import java.io.IOException; import org.apache.hadoop.conf.Configuration; ...
- 80端口占用异常解决方法java.net.BindException: Address already in use: JVM_Bind:80(或8080)
1:Tomcat(或其他Web容器)启动时控制台报错如下示: 2007-8-2 15:20:43 org.apache.coyote.http11.Http11Protocol init 严重: Er ...