当手机app应用需要更新时,如果需要重新下载一个完成的应用apk文件,十分消耗流量。这时候就可以使用增量更新技术。
增量更新可以使用bsdiff工具来完成,简单的说就是通过工具将旧的apk和新的apk进行比较,然后生成一个差异包,这个差异包的体积大小会远远地小于整个完整apk的大小。然后用户通过网络请求只需要下载这个差异包就可以完成应用的升级。
首先通过网络下载bsdiff工具
下载完成后进入文件夹,直接make编译是不行的,使用vim或编辑器打开MakeFile文件,发现格式有问题,定位到.ifndef WITHOUT_MAN和.endif前面,然后按tab键进行缩进。
然后保存.
发现直接make又报错。
第二个问题解决方法:打开bspatc,添加#include <sys/types.h>后解决
重新make后获得bsdiff和bspatch可执行文件。
使用Android Studio新建一个ndk项目,将bspatch.c文件和依赖的文件导入cpp文件夹下:
然后编辑CMakeLists.txt文件:
cmake_minimum_required(VERSION 3.4.1)
include_directories(bzip) #引入头文件
file(GLOB bzip_source_file bzip/*.c) #引入bzip中所有的c文件
add_library(
native-lib
SHARED
native-lib.cpp
bspatch.c
${bzip_source_file})
find_library(
log-lib
log)
target_link_libraries(
native-lib
${log-lib})
思路:用户手动点击更新按钮,然后从网络上下载差分包(patch又叫补丁),下载完成后发送Intent安装apk,覆盖本地的旧apk即可完成。
编辑native-lib.cpp:
#include <jni.h>
#include <string>
extern "C" {
extern int main(int argc, const char * argv[]); //导入bspatch文件的main函数
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_bsdiffdemo_MainActivity_getNewApk(JNIEnv *env, jobject thiz, jstring old_path,
jstring new_path, jstring patch) {
const char *charOldPath = env->GetStringUTFChars(old_path, 0);
const char *charNewPath = env->GetStringUTFChars(new_path, 0);
const char *charPatch = env->GetStringUTFChars(patch, 0);
const char *argv[] = {"",charOldPath, charNewPath, charPatch};
main(4, argv); //通过函数将旧的APK和差分包处理得到新的apk,并通过第2个参数输出出来
env->ReleaseStringUTFChars(old_path, charOldPath);
env->ReleaseStringUTFChars(new_path, charNewPath);
env->ReleaseStringUTFChars(patch, charPatch);
}
将旧的apk路径,下载完成的差分包路径和新apk的路径作为参数传给bspatch.c的main函数,main函数就会将旧apk和差分包通过处理得到新的apk,然后发送安装APK的Intent。
服务端(开发者):
将旧的apk和最新版本的apk,通过bsdiff工具得到差分包
命令./bsdiff old.apk new.apk patch
用户端
这个查分包被上传到网上,用户通过点击更新按钮:
public void update(View view) {
//模拟网络下载差分包
new AsyncTask<Void, Void, File>() {
@Override
protected File doInBackground(Void... voids) {
String oldApk = getApplicationInfo().sourceDir; //获取当前app(旧版)的apk的路径
//这里是模拟下载patch的网络请求过程
String patch = new File(Environment.getExternalStorageDirectory(), "patch").getAbsolutePath();
String newApk = createApk();
getNewApk(oldApk, newApk, patch);
return new File(newApk);
}
@Override
protected void onPostExecute(File file) {
if(file != null && file.exists()){
//发送安装apk的intent
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri fileUri;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
fileUri = FileProvider.getUriForFile(MainActivity.this,
getApplicationInfo().packageName + ".fileprovider", file);
}else{
fileUri = Uri.fromFile(file);
}
intent.setDataAndType(fileUri, TYPE);
startActivity(intent);
}else{
Log.d(TAG, "onPostExecute: " + "文件不存在!");
}
}
}.execute();
}
下载得到差分包patch后,调用bspatch.c的main函数获得新版的apk后,发送安装APk的Intent,完成版本的更新。
如图:
demo地址:https://github.com/lyx19970504/bsdiffDemo