使用 bsdiff 进行增量更新
2020-03-05 本文已影响0人
石器时代小古董
增量更新的原理
- 生成差异文件
- 下发差异文件到手机
- 客户端拿到差异包后和旧 APK 进行合成,生成新的 APK
- 客户端主动安装新的 APK
使用 bsdiff 生成差异包
使用 bsdiff 提供的工具生成差异包
./bsdiff oldfile newfile patchfile
客户端引用 bsdiff 的库
CMakeLists
include_directories(${CMAKE_SOURCE_DIR}/bzip2)
aux_source_directory(${CMAKE_SOURCE_DIR}/bzip2/ bzip2_src)
add_library(
native-lib
SHARED
native-lib.cpp
bspatch.c
${bzip2_src}
)
target_link_libraries(
native-lib
log)
native cpp 根据传入的旧 apk 路径,新 apk 路径,以及 patch 路径合成一个新的 apk
这里会将旧apk和patch合成,合成后存放的路径就是传入的 新 apk 地址
#include <jni.h>
#include <string>
// 声明一个函数
extern "C" {
// 声明一个函数 加上 extern 表示是在外部实现的
extern int bspatch_main(int argc, char *argv[]);
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_netease_bsdiff_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT void JNICALL
Java_com_netease_bsdiff_MainActivity_doPatchNative(JNIEnv *env, jobject instance, jstring oldApk_,
jstring newApk_, jstring patch_) {
const char *oldApk = env->GetStringUTFChars(oldApk_, 0);
const char *newApk = env->GetStringUTFChars(newApk_, 0);
const char *patch = env->GetStringUTFChars(patch_, 0);
//1 声明 , 实现
//2 声明&实现
char *argv[4] = {
"bspatch",//可以随意填""
const_cast<char *>(oldApk),
const_cast<char *>(newApk),
const_cast<char *>(patch)
};
bspatch_main(4, argv);
env->ReleaseStringUTFChars(oldApk_, oldApk);
env->ReleaseStringUTFChars(newApk_, newApk);
env->ReleaseStringUTFChars(patch_, patch);
}
Android 端拿到合成后的新 apk 就可以安装了
public void onUpdate(View view) {
//网络请求下载查分包(省略。直接拷贝查分包到sd卡)
new AsyncTask<Void, Void, File>() {
@Override
protected File doInBackground(Void... voids) {
//bspatch 做合成 得到新版本的apk文件
//sz: linux>windows
//rz: windows>linux
String patch = new File(Environment.getExternalStorageDirectory(),
"patch.diff").getAbsolutePath();
File newApk = new File(Environment.getExternalStorageDirectory(), "new.apk");
if (!newApk.exists()) {
try {
newApk.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
// 获取当前 apk 的安装路径
String oldApk = getApplicationInfo().sourceDir;
doPatchNative(oldApk, newApk.getAbsolutePath(), patch);
return newApk;
}
@Override
protected void onPostExecute(File apkFile) {
//安装
if (!apkFile.exists()) {
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 24) { //Android 7.0及以上
// 参数2 清单文件中provider节点里面的authorities ; 参数3 共享的文件,即apk包的file类
Uri apkUri = FileProvider.getUriForFile(MainActivity.this,
getApplicationInfo().packageName + ".provider", apkFile);
//对目标应用临时授权该Uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(apkFile),
"application/vnd.android.package-archive");
}
startActivity(intent);
}
}.execute();
}
private native void doPatchNative(String oldApk, String newApk, String patch);