一、 很久没有写博客了,一年了,最近辞职了,一直没有时间来提升自己,现在有时间来学习一下了。最近了解到一个技术叫做增量更新,自己学了一下,所以做个记录同时分享给大家。
二、 增量更新意思就是,两个版本的apk包做差分,提取出新包中增加的东西,作为一个差分包,从而使用户下载的更新包比新包更小,比如,你现在的版本是1.0,大小是20M,而你现在要发一个包大小是30M,如果要下载30M的包会使用更多的流量,耗时更多,这时,使用bsdiff做两者的差分包,此时该差分包的大小在10M左右,用户只需要下载该10M的包进行更新。这时客户端会使用bspatch进行合成一个新的完整包。使用的技术就是c/c++进行合成,在Android中要使用NDK技术。
三、 现在开始:
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
e、下载bzip2,因为bsdiff是依赖于bzip2的,所以下载方式为使用以下命令:
Centos: yum -y install bzip2-devel.x86_64
Mac: brew install bzip2
d、最后,make一下,完之后 ls一下:bsdiff bsdiff.1 bsdiff.c bspatch bspatch.1 bspatch.c Makefile
多了两个工具:bsdiff、bspatch,没错,就只要这两个,
f、做差分包是使用:./bsdiff oldapk newapk patch,这个命令中,oldapk是原来版本apk,newapk是更新的新版本apk,patch是使用该命令的生成的差分包。ok大功告成。至于怎么把文件导入导出Linux系统,可以使用Xftp工具。
第二种:在windows环境
去网上找bsdiff差分包生成工具,我这里有,后面附件给出来,
a、下载加压,解压后你会看到:
红色框里面的东西,没错哦,然后用cmd命令进入到该文件下,把,oldapk,newapk复制到该文件下,
使用命令:bsdiff oldapk newapk patch
完了之后会看到patch文件,没错,这就是差分包,ok,完事具备,只欠代码。
3、如果是新工程,只需要在new工程的时候选择native c++工程即可,如果是老项目没有使用到ndk的环境的,你先new一个native c++工程,把里面的cpp目录全部拷贝到工程对应位置,并且在app.gradle配置下面图片中的红框里面的代码即可:
4、把第一步我们下载bsdiff解压后的bspatch.c文件复制到cpp目录下,因为bspatch依赖bzip2里面的文件,然后下载bzip2下载地址:bzip2下载,解压,将里面的相关文件复制到项目中,建一个文件夹叫bzip,放在下面,相关文件为下图中红色框内所有文件:
此时同步一下,咦,发现patch一堆错误,不要慌,找到CMakeLists.txt文件,加入下面图中红色框里面的东西:
ok,你以为好了吗?别急,同步后发现还有错(新版本AndroidStudio),莫慌
因为新版本的studio原来代码的头文件引入格式找不到bzlib.h文件,所有把
#include
改成
#include "bzip/bzlib.h"
ok,最后环境配置好了,那么怎么用呢?
5、因为我们是用Java代码调用的,所以要写一个native方法来调用c/c++方法合成新的apk:
/**
* 合成差分包
* @param oldApk 旧版本的apk
* @param patch 拆分包,patch文件
* @param outPut 合成后新版本apk的输出路径
*/
private native void bsPatch(String oldApk,String patch,String outPut);
6、然后会在native-lib.cpp文件中生成对应的c++方法:
extern "C"
JNIEXPORT void JNICALL
Java_com_example_bsdiff_MainActivity_bsPatch(JNIEnv *env, jobject instance, jstring oldApk_,
jstring patch_, jstring outPut_) {
//将Java字符串转化成c/c++字符串(转化成UTF-8格式的char指针)
const char *oldApk = env->GetStringUTFChars(oldApk_, 0);
const char *patch = env->GetStringUTFChars(patch_, 0);
const char *outPut = env->GetStringUTFChars(outPut_, 0);
//释放指针
env->ReleaseStringUTFChars(oldApk_, oldApk);
env->ReleaseStringUTFChars(patch_, patch);
env->ReleaseStringUTFChars(outPut_, outPut);
}
那么,此时我们在这里去调用bspatch.c中的main方法,可以自己去看看这个源码,反正我是看不懂,调用该方法的时候还要引用该方法,具体如下图红色框内:
至于为什么main方法中第一个参数为4,因为你看源码中如果不为4、就直接抛异常了,所以直接填4,然后有的studio版本要强转一下argv参数,那就强转一下,好了,ndk环境和代码到此全部结束,接下来只需要写Java代码了。
7、先在MainActiviy中新建一个布局,显示版本号,和更新按钮:
点击更新的时候,我们从sd卡本地区加载patch差分包,就不模拟从网络了,当然实际开发中肯定是从网络上下载的
8、具体代码就不讲了,非常简单,可以实际开发中可以根据自己的需求去修改相应逻辑:
/**
* 使用异步任务加载差分包合成
*/
@SuppressLint("StaticFieldLeak")
private void update(){
new AsyncTask() {
@Override
protected File doInBackground(Void... voids) {
//合成文件
String oldApk = getApplicationInfo().sourceDir;
String patch = new File(Environment.getExternalStorageDirectory(),"patch").getAbsolutePath();
String outPut = createNewApk().getAbsolutePath();
bsPatch(oldApk,patch,outPut);
return new File(outPut);
}
@Override
protected void onPostExecute(File file) {
//安装apk
FileUtils.installApk(MainActivity.this,file);
}
}.execute();
}
/**
* 合成文件
* @return
*/
private File createNewApk() {
File newApk = new File(Environment.getExternalStorageDirectory(),"bsdiff.apk");
if (!newApk.exists()){
try {
newApk.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
return newApk;
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn:
update();
break;
}
}
9、需要注意事项:
a、在manifest文件中添加sd卡权限,安装包权限,同时还要兼容运行时权限
b、差分增量更新使用场景,如果合成的差分包大于新包的情况下不需要用增量更新,直接用全量更新
c、测试方法,打包两个版本,新版本(要修改版本名称和版本号),就版本大小最好有明显差异,用上面的方法生成差分包,然后将旧版本和差分包patch放到手机sd卡中,安装就版本apk,然后启动后,点击更新,后会合成新版本,然后安装成功,打开看到2.0版本。完事了。
10,源码以及资源附件:附件