NDK开发—增量更新

1、普通更新和增量更新

首先了解一下应用普通更新的逻辑(这里指不通过应用市场更新):
新版本发布后将APK文件上传到服务器。然后由客户端下载新的APK文件并安装。

但是如果APK过大,普通更新的弊端就出现了。
比如:一个游戏的APK,老版本有480M。新版本添加了一个模块APK增加到500M。按照普通更新的逻辑,用户需要下载500M的APK,很显然比较费流量!这个并不是只针对用户,对服务器也是如此,服务器也需要节省带宽。

假如有一种机制:只需要下载新版APK与老版APK多出的不同的部分,然后再由客户端将老版的APK和下载的部分合并成新版APK,再进行安装更新。

例如这个例子,用户只需要下载20M左右的更新包就能进行更新操作,就能非常愉快的解决流量的这个问题了。

以上的这个例子就是增量更新的基本逻辑,用图形来表示:

增量更新

根据增量更新的逻辑,可以知道增量更新重要的几个知识点。

  • 服务器根据新旧APK生成的增量包。(本文重点)
  • 客户端下载差分包(本文不做介绍)。
  • 客户端根据旧APK和下载的差分包生成新的APK。(本文重点,另外需要一点点NDK的基础)
  • 客户端安装新的APK。

2、增量更新的依赖

增量更新需要差分APK和合并APK,这里并不需要应用的开发者去写算法,直接可用三方提供的工具。

官方地址:
http://www.daemonology.net/bsdiff/

不过windows无法下载,可能被墙的缘故。我从第三方弄到的工具:

链接:拆分合并工具百度云地址

资源

这里集合了windows平台和linux平台使用的差分工具,以及客户端需要用到的合并工具。

3、差分APK

差分APK的操作有多种,我这里介绍windows平台和Linux平台两种。

3.1、Windows下的差分

将下载的工具中windows平台下的文件bsdiff4.3-win32-src.zip解压。


bsdiff for windows

然后再release文件夹下可以看到两个.exe文件。需要使用windows差分文件需要用到。bsdiff.exe.


bsdiff

接下来将旧版的APK和新版的APK复制到这个文件夹下,执行以下命令:
bsdiff.exe  旧APK地址  新APK地址 增量包地址
执行拆分命令

然后增量包就生成了:


生成增量包

以上方法应该是非常简单了。

当然解压bsdiff4.3-win32-src.zip之后会发现这是源码:


windows平台的源码
差分的效果

通过这个图可以看到,增量包要比新版本的安装包要小很多的。如果新版apk更大,效果更明显。

3.2、Linux下的差分

如果没有windows系统,linux系统同样是提供支持的。我这边是装了一个Ubuntu的虚拟机。由于linux不太熟练,我这里只展示linux差分的核心部分。

第一步将bsdiff-4.3.tar.gz和bzip2-1.0.6.tar.gz解压。将bsdiff-4.3.tar.gz中的bsdiff.c和bzip2-1.0.6.tar.gz中所有的.c和.h文件全部复制到一个目录中。(我这里是用windows解压的,所以放的是windows的截图)


bsdiff.c

bzip2的所有.h和.c

在Linux中新建一个文件夹存放这些文件:


在Linux的文件

然后需要用linux的编译命令gcc将这些文件编译成Linux中可执行的文件。(gcc是什么命令我就不介绍了)。接下来会踩几个坑。
接下来执行:

 gcc -g -o axe_bsdiff  blocksort.c decompress.c bsdiff.c  randtable.c  bzip2.c huffman.c compress.c bzlib.c crctable.c 

里面的axe_bsdiff是linux端可运行文件的文件名,可修改。执行这段命令就会遇到以下问题:

第一个坑

直接用vim进入bsdiff.c,找include bzlib.h的地方:
vim bsdiff.c

问题一目了然了,只有引用系统提供的头文件才能用"< >",那就把这里改成
include "bzlib.h"
然后保存!
这样也解决了上面为什么需要这么多文件的疑问了:bsdiff.c调用了bzlib下的c文件。
解决这个问题那就继续执行gcc命令。
第二个坑

以上就是第二个坑了,main方法被多次定义。这个问题也很清楚,每个程序只有一个main方法。那就把其他地方定义的main去掉就行了。这里需要保留bsdiff.c 中的main方法。

将不需要的main方法修改成其他方法

我这里用windows端的notepad++修改的。名字是随便改的,,只要符合规范就行了。
客户端合并也需要这些修改后的源文件,已上传git。最后有链接

修改完成之后,继续执行gcc编译命令。
然后就成功了,生成了axe_bsdiff的可执行文件!

gcc成功

接下来的步骤就和windows端差分增量包操作差不多了。执行一下命令:

./axe_bsdiff 旧APK路径 新APK路径 增量包路径

我这里将新旧APK都赋值到了这个文件夹,然后执行命令:


执行差分命令

成功生成增量包:


成功生成增量包

总体来说Linux差分增量包要比windows差分增量包麻烦些。还可能会遇到权限不够的问题,这个时候需要chmod命令等等。

4、客户端的合并操作

这里还是需要用到bsdiff-4.3.tar.gz和bzip2-1.0.6.tar.gz的解压文件。这里需要用到bsdiff-4.3中的bspatch.c还有bzip2-1.0.6所有的.c和.h。上面讲到的Linux中差分增量包的文件,这里的可以直接使用。

4.1、新建NDK项目 (这个就不讲解了)
4.2、将所需要的文件导入并且配置好CMakeLists.txt。
文件导入

CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1)

# 加载指定文件夹的所有文件
file(GLOB bzip2 src/main/cpp/bzip2/*.c)

find_library( log-lib
              log )

# 配置AxeBsPatch动态库
add_library( AxeBsPatch
             SHARED
             src/main/cpp/bspatch.c
             ${bzip2}
             )
# 链接AxeBsPatch动态库
target_link_libraries( AxeBsPatch
                       ${log-lib} 
4.3、新建native方法。

这边生成的native方法需要传入三个参数,老的APK地址、新的APK地址、下载的增量包的地址。

public class BsPatch {
    public native static int patch(String oldFile,String newFile,String patchFile);
    static {
        System.loadLibrary("AxeBsPatch");
    }
}

然后用javah命令生成.h文件。

4.4、实现.h文件中的方法

在bspatch.c中include生成的头文件。并实现.h中的方法。

#include "com_mg_axechen_bspatch_BsPatch.h"
// ... ... 省略若干代码

/*
 * Class:     com_mg_axechen_bspatch_BsPatch
 * Method:    patch
 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
 * params: old_path:The path for old APK.
 *         new_path:The path for new APK.
 *         patch_path:The path for patch package.
 */
JNIEXPORT jint JNICALL Java_com_mg_axechen_bspatch_BsPatch_patch
        (JNIEnv *env, jclass jclz, jstring old_path, jstring new_path, jstring patch_path) {

    // 先将jstring转化为char
    char *oldPath = (char *) (*env)->GetStringUTFChars(env, old_path, NULL);
    char *newPath = (char *) (*env)->GetStringUTFChars(env, new_path, NULL);
    char *patchPath = (char *) (*env)->GetStringUTFChars(env, patch_path, NULL);
    char *argv[4];

    int ret = -1;

    // 第一个参数随便填写
    argv[0] = "AXE_BSPATCH";
    argv[1] = oldPath;
    argv[2] = newPath;
    argv[3] = patchPath;

    // 第一个参数必须为4.
    ret = main(4, argv);

    // 回收资源
    (*env)->ReleaseStringUTFChars(env, old_path, oldPath);
    (*env)->ReleaseStringUTFChars(env, new_path, newPath);
    (*env)->ReleaseStringUTFChars(env, patch_path, patchPath);

    return ret;
}

这边也比较简单,将传入的参数放入数组中,然后调用bspatch中的main方法即可。

5、更新的逻辑判断

为了模拟得更像更新的逻辑,我加入了简单的VersionCode的判断。
老版APK的VersionCode是1 ,新版的版本VersionCode是2。如果是老版则更新。
因为更新耗时,里面包含了下载和合并。所以放在线程中。
如果合并成功,就通过Hanlder发送通知去安装新的APK。
我这里没有下载的逻辑,所以先将增量包放在了客户端sdcard的指定路径!

/**
     * 更新操作
     *
     * @param view
     */
    public void upload(View view) {
        if (APKUtils.getVersionCode(MainActivity.this, getPackageName()) >= 2) {
            //
            new AlertDialog.Builder(MainActivity.this)
                    .setMessage("已经是最新的版本了,无需更新!")
                    .setNegativeButton("确定", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    }).create().show();
            return;
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 下载就省略。 由于我没有服务器进行下载

                // 1、拿到老的APK
                String oldFile = APKUtils.getSourceApkPath(MainActivity.this, getPackageName());

                // 2、拿增量包的路径
                String patchFile = Contants.PATCH_FILE_PATH;

                Log.i("AXE", patchFile);
                // 3、开始执行合并操作
                int ret = BsPatch.patch(oldFile, Contants.NEW_APK_PATH, patchFile);

                // 4、合并的成功和失败
                if (ret == 0) {
                    Log.i("AXE", "合并成功");
                    handler.sendEmptyMessage(0);
                } else {
                    Log.i("AXE", "合并失败");
                }
            }
        }).start();
    }
4.6、安装APK

7.0后的APK安装有很大的改变。
看“在应用间共享文件”这部分
具体实现我也不贴出来了,这里直接贴出其他人的博客链接。
http://blog.csdn.net/czhpxl007/article/details/53781464

5、增量更新的优缺点

优点:

  • 很明显,能节约流量,节省服务器开支。

缺点:

  • 客户端和服务端需要加入相应的支持。每次发布新版本,服务端都需要为以前所有的老版本生成对应的差分包,并根据客户端端请求返回对应的更新包,维护过程将会变得相对复杂。客户端需要对差分包做更为详细的验证,防止出错,除此之外,客户端应该可以根据服务端更新开关来确定当前是使用完整更新还是增量更新。
  • apk包之间的差异过小时,比如2m以下,此时生成的差分包仍然有几百k,此时使用增量更新得不偿失,毕竟形成差分包和合并的过程都非常耗时。另外,但版本之间变化非常大的时候,通常是是大版本好变化的时候,比如从v 1.0.0到2.0.0,此时使用完整更新也不错。

以上缺点引用CSDN博客:

http://blog.csdn.net/dd864140130/article/details/52928419
作者:### 江湖人称小白哥

最后

本文讲解了增量更新的基本逻辑、差分操作(Windows/Linux)、合并的操作(客户端)、7.0后安装APK的改变。

客户端合并的源代码:
源码传送门

参考博客:
http://blog.csdn.net/lmj623565791/article/details/52761658
http://blog.csdn.net/dd864140130/article/details/52928419

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容