android升级优化之增量更新
一、 什么是增量更新?
我们一般版本升级,都需要先下载一个新包,下载完之后再安装覆盖旧包。下载一个完整的apk正常都是几十M,甚至几百M,这样既浪费时间又浪费流量。
这时候有人就会想,我们不是有旧的安装包,只要知道新安装包更新哪些内容,是不是只要下载这些需要更新的内容,而不用下载整个完整安装包。
这时就出现了增量更新,增量更新只需要下载差分包,即可完成更新。
二、增量更新原理
增量更新的原理,简单的说就是通过算法找出新版本和旧版本不一样的地方(这个过程也叫做差分),然后将不一样的地方抽取出来形成所谓的更新补丁(patch),也称之为差分包。客户端在检测到更新的时候,只需要下载差分包到本地,然后将差分包合并至本地的安装包,形成新版本的安装包,文件校验通过后再执行安装即可。
自从 Android 4.1 开始, Google Play 引入了应用程序的增量更新功能,App使用该升级方式,可节省约2/3的流量。
三、实现方案
3.1如何生成path差分包
macOS操作验证
-
安装
brew install bsdiff
-
Mac 安装brew环境,命令终端
/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"
-
生成差量文件
bsdiff 1.0.apk 2.0.apk out.patch
window操作验证
-
bsdiff 下载地址
链接: https://pan.baidu.com/s/1whjZccBKwcWjiXGxd9LzdQ?pwd=c5pw 提取码: c5pw -
然后打开Windows命令行工具,切换到该目录,打入命令bsdiff v1.0.0.apk v1.0.1.apk patch.patch生成差量文件,
bsdiff 1.0.apk 2.0.apk out.patch
3.2Android客户端的实现
1.得到out.path文件,只需要与旧包对比,合成出新包。
2.客户端要自行编译bspatch.c来实现合并差分包,也就是所谓的ndk开发,项目中导入bsdiff相关文件bspath.c, bzip包等(下载gitlub demo自取)
image.png
3.新建update-lib.cpp文件,执行out.path与旧包合成新包方法
image.png
image.png
4.增量更新代码实例
private void updateAPK() {
new AsyncTask<Void, Void, File>() {
@Override
protected File doInBackground(Void... voids) {
File oldFile = new File(Environment.getExternalStorageDirectory(), "1.0.apk");
Log.e(MainActivity.class.getSimpleName(), "oldFile是否存在:"+oldFile.exists());
String oldFilePath = oldFile.getAbsolutePath();
File patchFile = new File(Environment.getExternalStorageDirectory(), "out.patch");
Log.e(MainActivity.class.getSimpleName(), "patchFile是否存在:"+patchFile.exists());
String patchFilePath = patchFile.getAbsolutePath();
String outPath = createNewFile().getAbsolutePath();
PatchUtils.bsPatch(oldFilePath, patchFilePath, outPath);
return new File(outPath);
}
@Override
protected void onPostExecute(File file) {
super.onPostExecute(file);
if (file != null) {
if (!file.exists()) return;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Uri fileUri = FileProvider.getUriForFile(MainActivity.this, MainActivity.this.getApplicationInfo().packageName + ".fileprovider", file);
intent.setDataAndType(fileUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}
MainActivity.this.startActivity(intent);
} else {
Toast.makeText(MainActivity.this, "差分包不存在!", Toast.LENGTH_LONG).show();
}
}
}.execute();
}