增量更新

1. 概述


在我们开发的过程中,对于版本更新,按照我们一般套路都是在app刚一打开的时候,直接从服务器中下载最新的版本,然后下载安装就行。但是如果你app新的版本比较大,20M、30M的话,如果让用户下载,可能会比较耗流量、耗时间,需要用户去等待,所以这个时候就出现了增量更新。

2. 增量更新原理图如下


增量更新原理.png

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库
下载之后解压如下图:


图片.png

只留下 .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个东西;


图片.png

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: [email protected]
 * 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);
    }
}

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