Android 增量更新的实现

扯淡

最近闲来无事,学习了一下增量更新。

增量更新的概念就不扯了,有兴趣的看一下鸿洋大神的文章Android 增量更新完全解析 是增量不是热修复。

测试设备(Android 5.0系统)

Talk is cheap!废话不多说,接下来开始踩坑之路。

准备工具

  • bsdiff/bzip 密码: qdrb

    • 下载 bzip 后解压把里面的 .c 和 .h 文件复制到项目 app/src/main/jni/ 下面
    • bsdiff 文件中的 bsdiff.c 和 bspatch.c 文件复制到项目 app/src/main/jni/ 下面
  • Android Studio 配置 NDK 环境 Android NDK 开发从 0 到 1

生成增量文件

  • 写一个工具类 DiffUtil.java
    static {
        System.loadLibrary("bsdiff");
    }

    /**
     * native 方法 比较路径为 oldPath 的 apk 与 newPath 的 apk 之间差异,并生成 patch 包,存储于 patchPath
     */
    public static native int genDiff(String oldApkPath, String newApkPath, String patchPath);
  • 生成头文件
 在 Terminal 中进入到项目 ..app\src\main\java> 下输入 javah -d ../jni  包名.方法名

如: javah -d ../jni sj.updateplus_utils.DiffUtil
  • 复制头文件中代码到 bsdiff.c 中,之后再添加如下代码
JNIEXPORT jint JNICALL Java_sj_updateplus_utils_DiffUtil_genDiff
                 (JNIEnv *env, jclass cls, jstring old, jstring new, jstring patch) {
    int argc = 4;
    char * argv[argc];
    argv[0] = "bsdiff";
    argv[1] = (char*) ((*env)->GetStringUTFChars(env, old, 0));
    argv[2] = (char*) ((*env)->GetStringUTFChars(env, new, 0));
    argv[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0));

    printf("old apk = %s \n", argv[1]);
    printf("new apk = %s \n", argv[2]);
    printf("patch = %s \n", argv[3]);

    int ret = diffMain(argc, argv);

    printf("genDiff result = %d ", ret);

    (*env)->ReleaseStringUTFChars(env, old, argv[1]);
    (*env)->ReleaseStringUTFChars(env, new, argv[2]);
    (*env)->ReleaseStringUTFChars(env, patch, argv[3]);

    return ret;
}
  • bsdiff.c 中还需要修改的地方
//原代码:
int main(int argc, char *argv[]) {
    ...
}
//修改为:
int diffMain(int argc, char *argv[]) {
    ...
}
//原代码
#include 
//修改为,可自定义
#include "bzip2/bzlib.h"

最后再进行编译运行,这期间可能遇到各种报错,哈哈,没关系,反正我也不知道怎么解决。(。‿。开玩笑,后面有踩坑经历)

增量文件和 oldApk 的合并

这个和增量文件的生成类似

  • 写一个工具类 PatchUtil.java
    static {
        System.loadLibrary("bsdiff");
    }

    /**
     * native 方法 合并旧 apk 和补丁 patch,生成新的 apk
     * @param oldApkPath 旧apk文件路径
     * @param newApkPath 合并生成的新 apk 保存路径
     * @param patchPath  增量文件路径
     * @return 合并结果: 0-成功 -其他失败
     */
    public static native int mypatch(String oldApkPath, String newApkPath, String patchPath);`
  • 生成头文件
    同增量文件的生成里面一样
在 Terminal 中进入到项目 ..app\src\main\java> 下输入 javah -d ../jni  包名.方法名

如:javah -d ../jni sj.updateplus_utils.PatchUtil
  • 复制头文件中代码到 bspatch.c 里添加以下代码
JNIEXPORT jint JNICALL Java_sj_updateplus_utils_PatchUtil_mypatch
        (JNIEnv *env, jclass cls,
         jstring old, jstring new, jstring patch) {
    int argc = 4;
    char *argv[argc];
    argv[0] = "bspatch";
    argv[1] = (char *) ((*env)->GetStringUTFChars(env, old, 0));
    argv[2] = (char *) ((*env)->GetStringUTFChars(env, new, 0));
    argv[3] = (char *) ((*env)->GetStringUTFChars(env, patch, 0));

    int ret = patchMain(argc, argv);

    (*env)->ReleaseStringUTFChars(env, old, argv[1]);
    (*env)->ReleaseStringUTFChars(env, new, argv[2]);
    (*env)->ReleaseStringUTFChars(env, patch, argv[3]);
    return ret;
}
  • bspatch.c 中还需要修改的地方
//原代码
int main(int argc, char *argv[]) {
    ...
}
//修改为,可自定义
int patchMain(int argc, char *argv[]) {
    ...
}
//原代码:
#include 
//修改为:
#include "bzip2/bzlib.h"

踩过的坑

  • 编译缺少 xxx 文件
    CMakeLists.txt 文件中添加以下代码
add_library( # Sets the name of the library.
             bsdiff

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/jni/bzip2/blocksort.c
             src/main/jni/bzip2/bzip2.c
             src/main/jni/bzip2/bzip2recover.c
             src/main/jni/bzip2/bzlib.c
             src/main/jni/bzip2/compress.c
             src/main/jni/bzip2/crctable.c
             src/main/jni/bzip2/decompress.c
             src/main/jni/bzip2/dlltest.c
             src/main/jni/bzip2/huffman.c
             src/main/jni/bzip2/mk251.c
             src/main/jni/bzip2/randtable.c
             src/main/jni/bzip2/spewG.c
             src/main/jni/bzip2/unzcrash.c
             src/main/jni/bsdiff.c
             src/main/jni/bspatch.c )

CMakeLists.txt 中这两处地方要一致,可自定义

add_library( # Sets the name of the library.
             bsdiff
             ... )

target_link_libraries( # Specifies the target library.
                       bsdiff
                      ...)
  • 可能需要添加
    gradle.properties 文件
android.useDeprecatedNdk=true

进行 MD5 验证

这个步骤是为了检验合并生成的 apk 文件和我们要升级更新的 apk 文件是否一样
在 apk 文件目录下打开 cmd
输入命令行

//***.apk 为需要验证的 apk 文件名
certutil -hashfile ***.apk MD5
Android 增量更新的实现_第1张图片
image.png

写在最后

到这边增量更新操作基本就完成了。还有一些考虑不足之处。
当然这只是进行了增量文件的生成和新 apk 的合并,而且也只是在本地进行。其实呢,增量文件的生成最好在服务端进行,而且需要生成多个版本与最新版本的增量文件,才能保证各个版本的用户可以正常更新;更新完后还要安装等。
笔者这里是对其他 apk 的增量更新,如果是对本身 apk 的更新的话,还需要提取自身 apk 之类的,其他步骤可参照以上所写。

第一次写文章,还望各位指点

你可能感兴趣的:(Android 增量更新的实现)