浅析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( ...
随机推荐
- easyui combobox开启搜索自动完成功能
combo.json [{ "id":-1, "text":" ", "spell":"" },{ ...
- mt7620 wireless驱动特性意外发现
前言 今天又客户反映无线參数SSID编程了HT_AP0, 同事通过后台给他改动后反映给我,我想不正确啊,难道是无线驱动crash了?那应该不能玩才对啊... 追查线索 我们的路由器会定期汇报数据SSI ...
- MySQL:按前缀批量删除表格
想要实现mysql>drop table like "prefix_%" 没有直接可用的命令,不过可以通过mysql语法来组装, SELECT CONCAT( 'DROP T ...
- 32.NET中加密解密基本概念
对消息的接收方来说,安全的交流方式需要同时满足3个条件: 1.完整性:消息在传输途中没有被篡改过,即消息是完好无损的. 2.保密性:接收放可以理解或解密来自发送方的信息.(不保证第三方无法获得,但保证 ...
- java zxing实现二维码生成和解析zxing实现二维码生成和解析
原文:https://www.cnblogs.com/zhangzhen894095789/p/6623041.html zxing实现二维码生成和解析 二维码 zxing 二维码的生成与解析 ...
- bochs和硬盘管理
bochs和硬盘管理 实验一 目的:熟悉实验环境,认识Bochs虚拟机 内容: 1.下载并安装Bochs 2.3.7,官方网站 http://bochs.sourceforge.net/ 2.下载DO ...
- 如何让两个div并排,并且div要看得见边框
<div style="float:left; width:100px; height:100px; border:2px solid #0000FF;"></d ...
- Oracle常用系统查询SQL
以user1身份登录oracle,然后执行:select table_name from user_tables;或select table_name from tabs; 常用SQL --1.查询o ...
- 10个最好的 jQuery 视频插件
在这篇文章中已经收集了10个最佳的jQuery视频插件,帮助开发人员容易地实现网站播放影片功能.可以显示视频和视频播放列表. 1. Bigvideo.js BigVideo.js 是一个jQuery插 ...
- scala 学习笔记六 推导
1.介绍 在Scala中,推导将生成器.过滤器.和定义组合在一起. 2.例子 有一种将result用作val(而不是var)的方式,:“就地”构建result,而不是逐项构建,利用yield关键字,当 ...