基于Bsdiff技术的增量更新

1 篇文章 0 订阅
1 篇文章 0 订阅

版本信息

Binary diff/patch utility: 4.3

BZIP2 :1.0.6

编译bsdiff系统:centos

AndroidStudio:3.5

 

增量更新原理

新的apk包与老的apk包通过bsdiff工具生成增量更新包,下载增量更新包之后, 通过bspatch工具生成全量安装包。安装全量包即达到了增量更新的目的。

编译bsdiff

在linux中下载bsdiff,通过make命令编译。由于网站上下载的MakeFile编写问题,会报错。格式错误, shell语法的指令前面要有tab。


CFLAGS          +=      -O3 -lbz2

PREFIX          ?=      /usr/local
INSTALL_PROGRAM ?=      ${INSTALL} -c -s -m 555
INSTALL_MAN     ?=      ${INSTALL} -c -m 444

all:            bsdiff bspatch
bsdiff:         bsdiff.c
bspatch:        bspatch.c

install:
        ${INSTALL_PROGRAM} bsdiff bspatch ${PREFIX}/bin
        .ifndef WITHOUT_MAN
         ${INSTALL_MAN} bsdiff.1 bspatch.1 ${PREFIX}/man/man1
        .endif

 

修改之后在执行make命令,如果报以下错误, 说明bzlib没有安装。根据自己的系统安装bzlib。

bsdiff.c:33:19: fatal error: bzlib.h: No such file or directory
 #include <bzlib.h>
                   ^
compilation terminated.

安装完成再执行make命令,生成以下文件则编译成功。

cc -O3 -lbz2    bsdiff.c   -o bsdiff
cc -O3 -lbz2    bspatch.c   -o bspatch

生成差分包

通过命令生成差分包patch,自此服务器端的工作结束。android端需要下载这个差分包,通过bspatch工具合成全量包安装。

./bsdiff old.apk new.apk patch

Android端

新建C++项目,bsdiff源码中的bspatch.c文件放到项目中。将因为bspatch同样依赖bzlib,所以需要将bzlib的源码也下载下来供使用。下载地址可用文字开始的链接。最后项目的目录结构会如下图所示(bzlib源码已剔除不需要的部分):

编辑CMakeLists.txt,引入bzlib库和bspatch,完整代码如下所示:


cmake_minimum_required(VERSION 3.4.1)

file(GLOB bzip bzlib/*.c)

add_library(
        native-lib

        SHARED

        ${bzip}

        bspatch.c
        native-lib.cpp)

include_directories(bzlib)


find_library(
        log-lib

        log)


target_link_libraries(
        native-lib
        ${log-lib})

编写BsPatcher工具类,提供java调用的native方法,如下所示:

public class BsPatcher {
    static {
        System.loadLibrary("native-lib");
    }

      /**
     * 生成增量更新包
     * @param oldApkPath 老apk路径
     * @param newApkPath 生成的全量包路径
     * @param patchPath  从服务器上获取的差量包路径
     */
    public static native void update(String oldApkPath, String newApkPath, String patchPath);
}

编写native-lib.cpp文件,编写native方法,调用bspatch的main方法实现用old.apk和差分包合成全量包。代码如下所示(xxx是包路径中的一部分,涉及敏感信息,用xxx代替):

#include <jni.h>
#include <string>

extern "C" {
extern int main(int argc, const char *argv[]);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_xxx_hotfixdemo_BsPatcher_update(JNIEnv *env, jclass clazz, jstring old_apk_path,
                                           jstring new_apk_path, jstring patch_path) {

    const char *oldPath = env->GetStringUTFChars(old_apk_path, 0);
    const char *newPath = env->GetStringUTFChars(new_apk_path, 0);
    const char *patch = env->GetStringUTFChars(patch_path, 0);

    const char *argv[] = {"", oldPath, newPath, patch};
    main(4, argv);

    env->ReleaseStringUTFChars(old_apk_path, oldPath);
    env->ReleaseStringUTFChars(new_apk_path, newPath);
    env->ReleaseStringUTFChars(patch_path, patch);
}

 

编写MainActivity类,通过点击事件触发更新,为了简化下载差分包的过程,此demo将差分包已经放到SD卡的根目录。合成全量包是耗时操作,需要放到子线程工作,本demo使用AsynTask实现。代码如下所示:

@SuppressLint("StaticFieldLeak")
    public void update(View view) {
        new AsyncTask<Void, Void, File>() {
            @Override
            protected File doInBackground(Void... voids) {
                String patch = new File(Environment.getExternalStorageDirectory(), "patch").getAbsolutePath();
                String oldApk = getApplicationInfo().sourceDir;
                String newApk = createNewApk().getAbsolutePath();
                BsPatcher.update(oldApk, newApk, patch);
                return new File(newApk);
            }

            @Override
            protected void onPostExecute(File file) {
                super.onPostExecute(file);
                Intent intent = new Intent(Intent.ACTION_VIEW);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    /* Android N 写法*/
                    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    Uri contentUri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".fileprovider", file);
                    intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
                } else {
                    /* Android N之前的老版本写法*/
                    intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }
                startActivity(intent);
            }
        }.execute();
    }

    private File createNewApk() {
        File newApk = new File(Environment.getExternalStorageDirectory(), "bsdiff.apk");
        if (!newApk.exists()) {
            try {
                newApk.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return newApk;
    }

最终实现效果,点击升级,直接用patch和old.apk生成全量包,然后拉起安装程序进行安装。

 

注意:

需要读取权限,Android6.0及以上版本需要动态申请权限

Android7.0及以上安装需要FileProvider

Android8.0及以上安装需要在AndroidManifest.xml中声明权限:

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值