Android 增量更新

2,758 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

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下载的 bzip2bspatch.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 模拟升级

生成差分文件,放入本地目录测试升级