本文已参与「新人创作礼」活动,一起开启掘金创作之路
1、 什么是增量更新
简单的说就是 apk 更新的时候 不需要下载 新版本apk 只需要下载 旧版本和新版的 差分包,在手机本地 通过 差分包和旧app 合成新的apk,避免版本更新重新下载 新版本apk,造成的流量消耗和下载等待,现在大多数应用商店都已经做了相应的增量更新,如果我们的apk在我们的服务器就需要我们自己去实现。
2、Android 实现增量更新
增量更新是借助bsdiff 一个开源库实现的,这个开源库是根据文件二进制去实现 文件的 差分 ,具体介绍可以查找官网介绍
1、下载源码
官网下载不下来的可以通过 github去下载,下载的源码生成 bsdiff 、bspatch 的可执行文件
以上github下载下来是一个 c工程
1.1、生成bsdiff可执行文件
修改项目中 CMakeLists.txt 文件,然后 build下工程 ,cmake-build-debug 目录下就会生成相应的 可执行文件
cmake_minimum_required(VERSION 3.16)
project(untitled1 C)
set(CMAKE_C_STANDARD 99)
aux_source_directory(bzip2 SOURCE)
include_directories(bzip2) # 批量添加文件
add_executable(bsdiff bsdiff.c ${SOURCE}) # 生成可执行文件
可以通过cmd 命令生成差分文件
bsdiff old文件 new文件 差分文件
1.2、生成bspatch可执行文件
和生成bsdiff一样
cmake_minimum_required(VERSION 3.16)
project(untitled1 C)
set(CMAKE_C_STANDARD 99)
aux_source_directory(bzip2 SOURCE)
include_directories(bzip2) # 批量添加文件
add_executable(bspatch bspatch.c ${SOURCE}) # 生成可执行文件
可以通过cmd 命令生成新的文件
bsPatch old文件 new文件 差分文件
2、bspatch 合并功能合并到Android项目
通过命令生成差分文件 上传到后台,app通过下载对应版本的差分文件去合并成新的apk并安装,app需要用代码的方式去实现 差分合并
2.1 as新建C++ 工程
新建项目的时候选择C++工程即可
2.2 C 源码加到Android 工程 修改相关配置
之前github下载的 bzip2
和 bspatch.c
源文件 拷贝到 新建的Android C++ 工程中的cpp目录下需要配置CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.4.1)
aux_source_directory(bzip2 SOURCE)
add_library(
native-lib
SHARED
native-lib.cpp
bspatch.c
${SOURCE}
)
include_directories(bzip2)
find_library(
log-lib
log )
target_link_libraries(
native-lib
${log-lib} )
修改之后同步下工程 Build--> Refresh Linked C++ Projects
2.3 编写java层代码和native方法
编写 获取旧app和差分文件,生成新app目录,安装apk代码
package com.zyb.testdiff;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.widget.Toast;
import androidx.core.content.FileProvider;
import java.io.File;
public class AppUpDateUtils {
static {
System.loadLibrary("native-lib");
}
public static void updataApk(Context context){
// 0、获取旧的apk文件
String oldApkPath=context.getApplicationInfo().sourceDir;
//1、生成的apk文件
File newFile=new File(context.getExternalFilesDir("apk"),"app.apk");
//2、获取 增量更新包
File patchFile = new File(context.getExternalFilesDir("apk"), "patch.apk");
// 3\、 合成新的apk
int code= patchFile(oldApkPath,newFile.getAbsolutePath(),patchFile.getAbsolutePath());
// 4、安装新的apk
if (code==0){
installApk(context,newFile);
}else {
Toast.makeText(context,"增量更新失败",Toast.LENGTH_LONG).show();
}
}
private static void installApk(Context context, File newFile) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0+以上版本
Uri apkUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", newFile);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(newFile), "application/vnd.android.package-archive");
}
context.startActivity(intent);
}
public static native int patchFile(String oldApkPath, String newApkPath, String patchPath);
}
2.4 编写 JNI方法调用 bs库C代码
#include <jni.h>
#include <string>
#include <android/log.h>
extern "C"{
extern int patch(int argc, char *argv[]);
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_zyb_testdiff_AppUpDateUtils_patchFile(JNIEnv *env,
jclass clazz,
jstring old_apk_path,
jstring new_apk_path,
jstring patch_path) {
int argc=4;
char * argv[4];
argv[0]="bsPatch";
argv[1]= const_cast<char *>(env->GetStringUTFChars(old_apk_path, NULL));
argv[2]= const_cast<char *>(env->GetStringUTFChars(new_apk_path, NULL));
argv[3]= const_cast<char *>(env->GetStringUTFChars(patch_path, NULL));
int result=patch(argc,argv);
env->ReleaseStringUTFChars(old_apk_path, argv[1]);
env->ReleaseStringUTFChars(new_apk_path, argv[2]);
env->ReleaseStringUTFChars(patch_path, argv[3]);
return result;
}
2.5 模拟升级
生成差分文件,放入本地目录测试升级