java bsdiff_Android_增量更新(BSDiff)

本文介绍了Android增量更新的基本概念,重点解析了使用BSDiff库生成差分包的过程。通过在Linux环境下编译BSDiff,生成bsdiff和bspatch工具,服务端可以创建差分包,而Android客户端则通过JNI调用bspatch合成就新的APK,实现App的高效升级。
摘要由CSDN通过智能技术生成

Android中的增量更新指的是利用差分算法,计算两个App版本的差异,生成差分包,只需要下载差分包,不需要下载整个新版本的app,就可以完成App升级的方案,生成差分包不一定选用BSDiff(BSPatch,HPatch,XDelta等都可以),因为Android源码中使用的是BSDiff,所以本文简单分析BSDiff方式的增量更新。

流程图如下:20e055ac19ce13c600d189e079a1594d.png

BSDiff简介,也可以直接百度"BSDiff":

这个库主要用于对比生成差分包,其中用到了一个叫bzip2的库,用于文件压缩,其实Android源码这两个库都是存在的,虽然不能直接调用(因为开发者使用的是Android的SDK,不可能是完整的源码),Android源码中的路径版本\external\目录下(这个目录下还有okhttp,glide.zxing等等比较常见的库),版本过低可能会没有BSDiff库(Android6.0是有的)。

BSDiff的核心

尽可能多的利用old文件中已有的内容,尽可能少的加入新的内容来构建new文件(补丁),通常的做法是对old文件和new文件做子字符串匹配或使用hash技术,提取公共部分,将new文件中剩余的部分打包成patch包,在Patch阶段中,用copying和insertion两个基本操作即可 将old文件和patch包合成new文件(新Apk)

BSDiff基本步骤

对old文件中所有子字符串形成一个字典

对比old文件和new文件,产生diff string和extra string

将diffstring 和extrastring以及相应的控制字用zip压缩成一个patch包

服务端使用BSDiff

下载BSDiff,在Linux环境下使用,这里有个坑,BSDiff解压后的Makefile文件可能存在格式问题,如果make命令不能执行,修改后如下:

CFLAGS += -O3 -lbz2

PREFIX ?= /usr/local

INSTALL_PROGRAM ?= ${INSTALL} -c -s -m 555

INSTALL_MAN ?= ${INSTALL} -c -m 444

all: bsdiff bspatch

bsdiff: bsdiff.c

bspatch: bspatch.c

install:

${INSTALL_PROGRAM} bsdiff bspatch ${PREFIX}/bin

.ifndef WITHOUT_MAN

${INSTALL_MAN} bsdiff.1 bspatch.1 ${PREFIX}/man/man1

.endif

复制代码

然后执行make,生成bsdiff和bspatch文件,bsdiff就是用来做差分的,实际运用中应该是服务端做差分,bspatch是用来做合成的,就是Android端拿到差分包之后,和旧的apk文件合并生成新的apk。

可以通过bsdiff oldfile newfile patchfile命令生成差分包文件。如:

#patch为生成的差分包文件

./bsdiff old.apk new.apk patch

复制代码

Android合成新的APK文件

①新建cpp项目,将bspatch.c文件和bzip2相关的文件放在cpp目录下:bf3bda46dafa2b102df9d6c157656261.png

bzip2库中的文件比较多,demo中用到部分,可以直接查看bzip2下的Makefile文件,确定需要使用的文件(这里没有头文件)

2cf0130e5e7e7287e112968a728513be.png

② 在CMakeLists文件中引用,代码如下:

cmake_minimum_required(VERSION 3.4.1)

#导入bzip

file(GLOB bzib_source ${CMAKE_SOURCE_DIR}/bzip/*.c)

add_library(

native-lib

SHARED

native-lib.cpp

bspatch.c

#引用bzip

${bzib_source})

find_library(

log-lib

log)

target_link_libraries(

native-lib

${log-lib})

复制代码

注意: 导入引用同步后,bspatch.c文件中的#include 导入头文件可能有问题(不同版本),如果有问题将该行注释掉,下面爆红的地方快捷键导下包即可(就是正确引入头文件 如: #include "bzip/bzlib.h")

③Java层的逻辑,比较简单,这里没有请求服务端,本地取old.apk和patch文件,交给JNI合成新的apk文件bspatck.apk,然后执行安装操作,这里注意下8.0以上手机需要开启未知来源安装权限,跳转安装的逻辑在InstallUtils中,代码不贴了,需要完整代码的同学可以看文末链接,贴下MainActivity的代码

// Used to load the 'native-lib' library on application startup.

static {

System.loadLibrary("native-lib");

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

TextView tVersion = findViewById(R.id.tv_version);

tVersion.setText(BuildConfig.VERSION_NAME);

final Button btUpdate = findViewById(R.id.bt_update);

btUpdate.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

requestPerms();

}

});

}

private void requestPerms() {

//权限,简单处理下

if (Build.VERSION.SDK_INT>Build.VERSION_CODES.N) {

String[] perms= {Manifest.permission.WRITE_EXTERNAL_STORAGE};

if (checkSelfPermission(perms[0]) == PackageManager.PERMISSION_DENIED) {

requestPermissions(perms,200);

}else {

update();

}

}

}

//从本地内存取patch差分文件和旧的apk文件合并

private void update() {

new AsyncTask() {

@Override

protected File doInBackground(Void... voids) {

//私有目录下的apk文件

String oldApk = getApplicationInfo().sourceDir;

//获取差分包的绝对路径

String patch= new File(Environment.getExternalStorageDirectory(),"patch").getAbsolutePath();

//合成的新apk的存放路径

String output=createNewApk().getAbsolutePath();

bsPatch(oldApk,patch,output);

return new File(output);

}

@Override

protected void onPostExecute(File file) {

//合成后安装

InstallUtils.installApk(MainActivity.this,file);

}

}.execute();

}

private File createNewApk() {

File newApk = new File(Environment.getExternalStorageDirectory(),"bspatch.apk");

if (!newApk.exists()) {

try {

//占位

newApk.createNewFile();

} catch (IOException e) {

e.printStackTrace();

}

}

return newApk;

}

public native void bsPatch(String oldApk,String patch,String output);

复制代码

④JNI合成新的安装包,代码比较简单,直接贴一下吧,就是使用bspatch直接合成

注意:代码中调用的bspatch_main方法就是bspatch的main方法,重命名了一下,建议改下名,养成好习惯,因为实际项目中可能用到多个JNI库,都有main方法

#include

#include

//导入bspatch.c中要使用的方法

extern "C"{

int bspatch_main(int argc, char **argv);

}

extern "C"

JNIEXPORT void JNICALL

Java_com_jni_bsdiffdemo_MainActivity_bsPatch(JNIEnv *env, jobject instance, jstring oldApk_,

jstring patch_, jstring output_) {

//相当于类型转换,转换成C/C++识别字符串

const char *oldApk = env->GetStringUTFChars(oldApk_, 0);

const char *patch = env->GetStringUTFChars(patch_, 0);

const char *output = env->GetStringUTFChars(output_, 0);

const char *arg[]={"bisdff",oldApk,output,patch};

//第一个参数传4(否则直接抛错),第二个参数是数组如上

bspatch_main(4, const_cast(arg));

//释放指针

env->ReleaseStringUTFChars(oldApk_, oldApk);

env->ReleaseStringUTFChars(patch_, patch);

env->ReleaseStringUTFChars(output_, output);

}

复制代码

最终实现效果如图,为了展示效果,new.apk相对于old.apk改了下versionName:5fdca7fa0b54d3d34ee5008783d03706.gif

完整代码请移步:Github

注:所用到的bzip2库和bsdiff库文件放在了bsdiff目录下~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值