一、增量更新原理
-
1.增量更新原理
bspatch依赖bzip2进行压缩 使用哈夫曼算法 主要用来做差分和合并的
bspatch官网:(需要翻墙)
http://www.daemonology.net/bsdiff/bzip2 官网:
http://www.bzip.org/downloads.html
-
2.普通更新流程图
-
3.增量更新流程图
- 4.哈夫曼算法
- 5.热修复、插件化和增量更新的区别
二、生成windows环境的差分工具
- 1.下载Windows环境的源码
-
2.创建VS工程
为了方便管理在自己的VS工程里面创建include 和 src 两个文件夹
将头文件放入include 源文件放入src
- 3.关联头文件
- 4.关联源文件
- 5.配置VS头文件路径(引入include目录)
-
6.VS解决 _CRT_SECURE_NO_WARNINGS 警告
右键工程 ---> 属性 ---> c++ -----> 命令行 添加 -D _CRT_SECURE_NO_WARNINGS
-
7.关闭sdl 安全检查
error C4996: 'setmode': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _setmode. See online help for details. d:\develop\visual_studio\workspacec++\hubindiffdemo\hubindiffdemo\src\bzlib.c 1422 1 hubinDiffDemo
-
8.运行成功后生成了差分工具 hubinDiff.exe 位于Debug目录下(也可以配置生成hubinDiff.dll动态库,切换平台后需要重新设置以上步骤)
cd到差分工具所在路径下为apk生成差分包 hubinDiff.exe appOld.apk appNew.apk app.patch
三、windows服务器jni动态生成差分包
-
1.定义native方法
/* *oldPath :旧版本apk路径 *newPath :新版本apk路径 *patchPath:差分包生成路径 **/ public static native void diff(string oldPath,String newPath,String patchPath);
2.JNI层调用差分算法
//将第bsdiff.cpp源文件228行main()入口函数:int main(int argc,char *argv[])
//更名为int bsdiff_main(int argc,char *argv[])
JNIEXPORT void JNICALL Java_hubin_diff
(JNIEnv *env, jclass jazz,jstring oldPath_jst, jstring newPath_jst, jstring patchPath_jst)
{
int argc = 4;
char *argv[4];
char *oldPath = (char*)env->GetStringUTFChars(oldPath_jst, NULL);
char *newpath = (char*)env->GetStringUTFChars(newPath_jst, NULL);
char *patchPath = (char*)env->GetStringUTFChars(patchPath_jst, NULL);
argv[0] = "bsdiff_Tim";
argv[1] = oldPath;
argv[2] = newpath;
argv[3] = patchPath;
bsdiff_main(argc,argv); //调用差分算法
env->ReleaseStringUTFChars(oldPath_jst, oldPath);
env->ReleaseStringUTFChars(newPath_jst, newpath);
env->ReleaseStringUTFChars(patchPath_jst, patchPath);
}
四、生成Linux环境的差分工具
1.在官网下载Linux平台的bspatch源码
2.拷贝bsdiff里面的源文件和bzip2里面的.c和.h源文件
- 3.将bsdiff.c的源文件中的 #include <bzlib.h> 改成 #include "bzlib.h"
4.将除了bsdiff.c的其他源文件中的main方法改成其它名字
5.将所有源文件传输到linux服务器的文件夹下(/home/Bsdiff)
6.cd到Bsdiff文件夹下修改其权限为全部可执行 命令:chmod 777 ./*
-
7.使用gcc编译生成hubinBsDiff, 下面命令行中“...”表示其它所有的源文件
gcc fPIC blocksort.c decompress.c bsdiff.c randtable.c bzip2.c huffman.c compress.c bzlib.c crctable.c -o hubinBsDiff
-
8.使用hubinBsDiff生成差分包
hubinBsDiff appOld.apk appNew.apk app.patch
-
9.补充:如果需要生成so库,需要引入jni头文件,已经用javah生存的头文件并使用如下命令:
gcc -I/usr/local/jdk/jdk1.8.0_152/include/linux -I/usr/local/jdk/jdk1.8.0_152/include -fPIC blocksort.c decompress.c bsdiff.c randtable.c bzip2.c huffman.c compress.c bzlib.c crctable.c -shared -o libhubiniBsDiff.so 备注:-I/usr/local/jdk/jdk1.8.0_152/include/linux 和 -I/usr/local/jdk/jdk1.8.0_152/include表示jni头文件所在的路径
五、android平台合并
- 1.同上步骤,拷贝所有linux版本源码至工程的cpp目录下重命名所有源文件中的main方法,引入正确的头文件路径,
-
2.配置CmakeList
cmake_minimum_required(VERSION 3.4.1) file(GLOB my_c_path src/main/cpp/bzip2/*.c) add_library( TimBisPatch SHARED ${my_c_path} #添加bzip2目录下的所有源文件 src/main/cpp/bspatch.c) find_library( log-lib log ) target_link_libraries(TimBisPatch ${log-lib} )
-
3.java层native方法
public native static int patch(String oldfile, String newFile, String patchFile);
4.JNI层调用bspatch.c中改名后的main方法
JNIEXPORT jint JNICALL Java_hubin_BsPatch_patch
(JNIEnv *env, jclass jazz, jstring oldPath_jstr, jstring newPath_jstr, jstring patchPatch_jst) {
int ret= -1;
LOGD(" jni patch begin");
char *oldPath = (*env) -> GetStringUTFChars(env, oldPath_jstr, JNI_FALSE);
char *newPath = (*env) -> GetStringUTFChars(env, newPath_jstr, JNI_FALSE);
char *patchPath = (*env) -> GetStringUTFChars(env, patchPatch_jst, JNI_FALSE);
int argc = 4;
char *argv[4];
argv[0] = "hubinBsPatch"; //随便定义一个名字
argv[1] = oldPath; //老版本apk路径
argv[2] = newPath; //新版本apk路径
argv[3] = patchPath; //差分包所在路径
//如果成功ret等于0
ret = bspatch_main(argc,argv); //调用合并算法
(*env) -> ReleaseStringUTFChars(env, oldPath_jstr, oldPath);
(*env) -> ReleaseStringUTFChars(env, newPath_jstr, newPath);
(*env) -> ReleaseStringUTFChars(env, patchPatch_jst, patchPath);
return ret;
}
-
5.剩下的java层下载差分包,安装apk的代码省略(7.0以上安装有点小区别)。。。
7.0以上安卓 apk安装可以参考博客:http://blog.csdn.net/yulianlin/article/details/52775160
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
public class ApkUtils {
//获取APK版本号 根据 key uuid判断(渠道 版本)
public static int getVersionCode (Context context, String packageName) {
PackageManager pm = context.getPackageManager();
try {
PackageInfo info = pm.getPackageInfo(packageName, 0);
Log.d("Tim","getVersionCode = "+info.versionCode);
return info.versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取已安装Apk文件的源Apk文件
* 如:/data/app/my.apk
*
* @param context
* @param packageName
* @return
*/
public static String getSourceApkPath(Context context, String packageName) {
if (TextUtils.isEmpty(packageName))
return null;
try {
ApplicationInfo appInfo = context.getPackageManager()
.getApplicationInfo(packageName, 0);
return appInfo.sourceDir;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 安装Apk
*
* @param context
* @param apkPath
*/
public static void installApk(Context context, String apkPath) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://" + apkPath),
"application/vnd.android.package-archive");
context.startActivity(intent);
}
}