使用 bsdiff 进行增量更新

2020-03-05  本文已影响0人  石器时代小古董

增量更新的原理

  1. 生成差异文件
  2. 下发差异文件到手机
  3. 客户端拿到差异包后和旧 APK 进行合成,生成新的 APK
  4. 客户端主动安装新的 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);
上一篇下一篇

猜你喜欢

热点阅读