扯淡
最近闲来无事,学习了一下增量更新。
增量更新的概念就不扯了,有兴趣的看一下鸿洋大神的文章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
image.png
写在最后
到这边增量更新操作基本就完成了。还有一些考虑不足之处。
当然这只是进行了增量文件的生成和新 apk 的合并,而且也只是在本地进行。其实呢,增量文件的生成最好在服务端进行,而且需要生成多个版本与最新版本的增量文件,才能保证各个版本的用户可以正常更新;更新完后还要安装等。
笔者这里是对其他 apk 的增量更新,如果是对本身 apk 的更新的话,还需要提取自身 apk 之类的,其他步骤可参照以上所写。
第一次写文章,还望各位指点