1. 概述
在我们开发的过程中,对于版本更新,按照我们一般套路都是在app刚一打开的时候,直接从服务器中下载最新的版本,然后下载安装就行。但是如果你app新的版本比较大,20M、30M的话,如果让用户下载,可能会比较耗流量、耗时间,需要用户去等待,所以这个时候就出现了增量更新。
2. 增量更新原理图如下
3. 增量更新算法核心
比如用户手中当前版本是1.0,服务器中是2.0,并且服务器中肯定还保留着1.0版本:
1>:在服务器中比较两个文件的不同,比如说修改了一些代码,这个时候两个版本可能就有一点不同,根据这些差异的代码会生成一个差分包;
2>:客户端下载差分包,然后进行合并,生成新的2.0版本,然后进行安装;
4. 实现步骤
1>:写Java的native方法;
2>:生成头文件:
2.1>>:把项目rebuild一下,确保生成PatchUtil.class文件;
2.2>>:在AS控制台的Terminal下边敲命令:
cd E:\code\PatchDemo\app\build\intermediates\classes\debug 敲回车,进入到目录中;
2.3>>:javah cn.novate.patch.PatchUtils 敲回车,这个时候会生成一个 头文件 com_hc_essay_library_util_PatchUtils.h;
2.4>>:新建jni目录,然后把这个文件复制到 jni目录下边
3>:下载第三方的 C库
下载之后解压如下图:
只留下 .c文件,把红色部分删除,然后把合并 的 bspatch.c文件拷贝到 jni目录下就可以
3.1>>:下载第三方提供好的 C库;
3.2>>:下载增量更新的 bsdiff,依赖bzdiff;
3.3>>:下载bzdiff后解压,只留下 .h和 .c文件,其余全删
4>:配置NDK环境
4.1>>:复制Android.mk、Application.mk文件到 jni目录;
4.2>>:复制下边内容到 build.gradle下边,原模原样复制即可:
sourceSets {
main {
jni.srcDirs = []//设置禁止gradle生成Android.mk
jniLibs.srcDirs = ['libs']
}
}
task ndkBuild(type: Exec) {//设置新的so的生成目录
commandLine "D:\\Android\\sdk\\ndk-bundle\\ndk-build.cmd",
'NDK_PROJECT_PATH=build/intermediates/ndk',
'NDK_LIBS_OUT=libs',
'APP_BUILD_SCRIPT=jni/Android.mk',
'NDK_APPLICATION_MK=jni/Application.mk'
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
如果还没有配置NDK环境的,可以现在 setting中下下载CMake、LLDB、NDK这3个东西;
4.3>>:在gradle.properties文件加下边的一句代码:
android.useDeprecatedNdk=true
4.4>>:Android.mk如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE :=bspatch // 这个待会是生成的 .so库的名称
LOCAL_SRC_FILES :=bspatch.c // 你要编译的文件
LOCAL_LDLIBS := -ljnigraphics -llog
include $(BUILD_SHARED_LIBRARY)
运行一次,如果能运行起来,说明整合第三方的C库文件没问题了。
5>:合并方法的实现
就是需要调用 jni目录下的 bspatch.c文件中的 combine()方法,可以先去大概阅读下 jni目录下的 bspatch.c文件中的 combine()方法的参数,然后调用
5. 服务器生成差分包,可以用 eclipse 代替服务器,供app下载,自己写好编译成 dll动态库给服务器
1>:下载https://github.com/curasystems/bsdiff-win
Java语言调用 C语言
2>:新建Java工程,编写native方法
3>:生成头文件,和上边方式一样,使用 cmd命令,进入到项目的 src目录,命令是 javah 包名+类名;
4>:生成 dll动态库,供java调用,必须借用 VisualStudio2013或者VisualStudio2015都可以,新建VS工程;
5>:把下载好的 win .c库文件 拷贝到 VS工程目录;
6>:在VS中添加 头文件和源文件;
7>:运行一下,
#define _CRT_SECURE_NO_WARNINGS
过时的函数
-D _CRT_NONSTDC_NO_DEPRECATE
一些不安全的函数
8>:解决完错误以后,生成dll动态库,没报错代表整合第三方的C库是没问题的;
9>:把头文件 copy到VS工程目录,在VS添加 头文件和源文件;
10>:jni.h找不到,直接 在java安装目录下搜索,然后拷贝;
11>:写生成差分包的实现方法,首先先阅读 main()方法,然后调用C方法;
12>:在Java文件中调用生成好的 dll 动态库;
13>:在 eclipse 中作为服务器中,把Version_1.0.apk和version_2.0.apk都放在eclipse中,然后在eclipse中生成一个差分包 version_1.0_2.0.patch,然后手动把 这个差分包复制到 手机存储目录中,就表示 客户端已经成功的下载了 服务器生成的差分包然后保存到手机存储目录中
6. 校验签名
1>:去网上找系统应用的所有源码;
因为安装apk应用其实是我们系统的 Activity的完成,它会去校验签名,仿照它获取就ok,其实都是通过反射获取的
7. 代码流程如下:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/1 17:25
* Version 1.0
* Params:
* Description:
*/
public class MainActivity extends AppCompatActivity {
//
private String mPatchPath = Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator + "version_1.0_2.0.patch" ;
private String mNewApkPath = Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator + "Version2.0.apk" ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 测试增量更新
initData() ;
}
private void initData() {
// 1. 在刚进入app时,访问后台接口,判断是否需要更新版本
// 2. 如果需要更新版本,就提示用户需要下载,
// 或者在后台直接下载,然后提示用户更新
// 3. 下载完差分包之后,调用我们的方法去合并生成新的apk
// 合并两个apk是一个耗时操作
// 获取本地的apk路径 就是已经被安装了的1.0
// 即就是 获取本地的getPackageResourcePath() apk的路径
// 调用完下边这个方法后,会生成一个差分包
// 如果文件不存在
if (!new File(mPatchPath).exists()){
return;
}
// 下载完差分包,保存到手机目录中,在app直接运行到首页时,直接合并然后安装
// 在version_1.0.apk和version_2.0.apk合并完之后,就手动删除差分包,
// 否则下一次打开app时候,2.0会继续与查分包进行合并,就会报错
PatchUtils.combine(getPackageResourcePath() , mNewApkPath , mPatchPath);
// 4. 需要校验签名 就是获取本地apk的签名 与我们新版本apk做对比,
// 如何获取 2.0版本apk的签名
// 5. 安装最新版本 (百度搜索如何安装apk)
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(mNewApkPath)),
"application/vnd.android.package-archive");
startActivity(intent);
}
}