原文地址:https://apkdv.com/android-incremental-update.html
应用越做越大,可能只是一个小小的改动就需要重新下载安装整个APP,这种方式即增加了服务器的压力,又浪费了用户的流量,因此每当我们发布新版本的时候,一些用户升级并不是很积极,这就造成了新版本的升级率并不高。而google为了解决了这个问题,提出了Smart App Update,即增量更新(也叫做差分升级)。
增量更新的原理非常简单,简单的说就是通过某种算法找出新版本和旧版本不一样的地方
(这个过程也叫做差分,某种。。别问我是那种 因为我也不知道。。),然后将不一样的地方抽取出来形成所谓的更新补丁(patch)。客户端在检测到更新的时候,只需要下载差分包到本地,然后将差分包合并至本地的安装包,形成新版本的安装包,文件校验通过后再执行安装即可。本地的安装包通过提取当前已安装应用的apk得到。
现在的问题在于如何生成差分包以及合并差分包。这里,我们借助开源库bsdiff来解决以上两个问题。首先我们先演示一下差分包的形成与合并。 我们先打出一个安装包,假设为old.apk。对源码做修改后,再打出一个新的安装包new.apk。此处old.apk相当于老版本的应用,而new.apk相当于新版本的应用。接下来,我们利用bsdiff来生成差分包patch.patch。
将上面的old.apk和new.apk放入bsdiff解压后的目录,然后在控制台中执行命令bsdiff old.apk new.apk patch.patch,稍等一会便可以生成差分包patch.patch,如下
使用命令bsdiff old.apk new.apk update.patch
便可以生成一个名为update.patch的补丁。
合并old.apk和patch.patch,生成新的安装包new.apk。只要此处合并出来的new.apk和上面我们自己打出来的new.apk(MD5)一样,那么就可以认为它就是我们需要的新版本安装包。
将old.apk和patch.patch放入bsdiff文件夹,执行命令:
bspatch old.apk new.apk update.patch
,稍等一会之后便可以看到合并出的new.apk.
我们可以安装测试APP是否正常。
客户端支持增量更新总体和上面的演示差不多,唯一的区别在于客户端要自行编译bspatch.c来实现合并差分包,也就是所谓的ndk开发,这里我们首先要下载bsdiff的源码以及bszip的源码。
将bzip中除了.c .h 外的文件全部删除,将整个文件夹复制到AS的JNI目录,同时将bsdiff的源码复制到JNI目录,将bsdiff的代码打开将main方法改成updatePatch
目录结构应该是这样的:
bspatch_util其实就是对接收到的Java参数的接收
#include "com_lengyue_update_PatchUpdateUtil.h"
JNIEXPORT jint JNICALL Java_com_lengyue_update_PatchUpdateUtil(JNIEnv *env, jclass jclass1,
jstring old, jstring new,
jstring patch){
char *argv[4];
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));
//此处updatePatch()就是上面我们修改出的
int result = updatePatch(4, argv);
(*env)->ReleaseStringUTFChars(env, old, argv[1]);
(*env)->ReleaseStringUTFChars(env, new, argv[2]);
(*env)->ReleaseStringUTFChars(env, patch, argv[3]);
return result;
}
接下来我们编译项目就可以了
我们假设我们已经下载了差分包(生产环境需要对下载的差分包做MD5校验),原来的APK直接在本地提取就好了。 我们安装的应用通常在、data/app下,可以通过一下代码获取其路径:
context.getPackageManager().getApplicationInfo(packageName, 0).sourceDir
然后可以通过PatchUtil.patchUpdate(String oldApkPath,String newApkPath,String pathPath)来进行合并了。此处需要注意:合并的过程比较耗时,需要放到子线程中进行。
需要注意的是:在合并完成后首先要做的就是对合并的APK进行MD5校验,校验通过之后,再进行安装:
/**
* 安装Apk
*
* @param context
* @param apkPath
*/
public static void installApk(Context context, String apkPath) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://" + apkPath),
"application/vnd.android.package-archive");
context.startActivity(intent);
}
到现在,增量更新已经完成。现在可以把增量包以及合并之后的安装包进行删除了。
demo已经上传到Github: