Android增量更新原理及Kotlin实现

Android增量更新原理及实现方法

2018-4-23 by 大强

一、前言:

   Android客户端需要频繁更新版本,每次更新都要下载新apk,1是浪费流量,2是我的服务器是低配,下载速度慢,怎么节省流量呢?增量更新,学习下其原理及实现过程。

二、几个概念:

  • 热修复/热更新 轻量级 的更新,抢先上线,打补丁
  • 插件化:为提高开发效率,每个人做一个模块,解耦模块,模块更新
  • 增量更新: 基础技术,属于重量级的更新
三、传统的版本更新流程
Created with Raphaël 2.1.2 服务器 服务器 客户端 客户端 上传新版本,旧版本20M,新版本30M 是否有新版本? 有更新 下载apk(30M) apk安装
四、增量更新流程
Created with Raphaël 2.1.2 服务器 服务器 客户端 客户端 新版本(30M)和旧版本(20M)做差分,生成差分包(8M) 是否有新版本 ? 有新版本 下载差分包8M 将旧版本与差分包合并,生成新的apk,安装
五、疑问:差分包大小是 新版本和旧版本大小的差吗?

差分流程:

   newApk与oldApk相同的部分会已地址的方式放入差分包里,不同的部分,经过压缩放到差分包里,以双向链表保存:压缩部分的地址<–>压缩部分地址。

六、实现,开始撸码

1. 二进制文件 拆分/合并 的工具地址:http://www.daemonology.net/bsdiff/

bsdiffbspatch 用的是 bzip2

需要下载2个文件:

bsdiff-4.3.tar.gz: http://www.daemonology.net/bsdiff/bsdiff-4.3.tar.gz

bzip2:

http://www.bzip.org/downloads.html
http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
2.下面用Kotlin语法来实现增量更新:
  • 新建一个Android工程,勾选Include C++ support 和 Include Kotlin support 选项
    • 新建BsPatch类
public class BsPatch {
    public static native int bsPatch(String oldPath,String newPath,String patchPath);

    static {
        System.loadLibrary("native-lib");
    }
}
  • 生成对应的c++方法
    native-lib.cpp
#include <jni.h>

extern "C"
JNIEXPORT jint JNICALL
Java_com_hbck_bspatchdiff_BsPatch_bsPatch(JNIEnv *env, jclass type, jstring oldPath_,
                                          jstring newPath_, jstring patchPath_) {
    const char *oldPath = env->GetStringUTFChars(oldPath_, 0);
    const char *newPath = env->GetStringUTFChars(newPath_, 0);
    const char *patchPath = env->GetStringUTFChars(patchPath_, 0);

    // TODO

    env->ReleaseStringUTFChars(oldPath_, oldPath);
    env->ReleaseStringUTFChars(newPath_, newPath);
    env->ReleaseStringUTFChars(patchPath_, patchPath);
}
  • 解压bsdiff-4.3.tar.gz ,bzip2-1.0.6.zip
  • 拷贝所有的.c和.h文件到cpp目录下,如下图:
    这里写图片描述
  • 修改CMakeLists.txt,配置下添加的c代码
file(GLOB bzip_c src/main/cpp/bzip2/*.c)
add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
              ${bzip_c}
             src/main/cpp/bspatch.c
             src/main/cpp/native-lib.cpp )
  • Build->Rebuild project,报错,如下图,这是因为c文件里有多个入口main函数,重复导致的,把所有的main方法修改下,我这里以c文件的名字命名了。
    这里写图片描述

  • bspatch.c分析
    apk合并的方法在bspatch里,
    int bspatch(int argc, char *argv[]) 这个方法,直接调用这个方法,argc是参数的个数,argv是参数数组
    if (argc != 4) errx(1, “usage: %s oldfile newfile patchfile\n”, argv[0]);
    这行代码说明参数是4个,依次为报错信息,旧apk路径,新apk路径,差分包路径

  • 贴出native-lib的代码,一般拆分是服务端做的,可以用dsdiff.exe以命令方式生成差分包,然后上传到服务器,Android端版本更新时,下载差分包,然后和旧的安装包进行合并,生成新的apk

#include <jni.h>
#include "android/log.h"

#ifdef __cplusplus
extern "C" {
#endif
int bspatch(int argc, char *argv[]);
int bsdiff(int argc,char *argv[]);
#ifdef __cplusplus
}
#endif


#define TAG "daqiang_bspatch"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG,  __VA_ARGS__ )

extern "C"
JNIEXPORT jint JNICALL
Java_com_hbck_bspatchdiff_BsPatch_bsPatch(JNIEnv *env, jclass type, jstring oldPath_,
                                          jstring newPath_, jstring patchPath_) {
    LOGD("native patch begin");
    char *oldPath = (char *) env->GetStringUTFChars(oldPath_, 0);
    char *newPath = (char *) env->GetStringUTFChars(newPath_, 0);
    char *patchPath = (char *) env->GetStringUTFChars(patchPath_, 0);

    int argc = 4;
    char *argv[4];
    argv[0] = TAG;
    argv[1] = oldPath;
    argv[2] = newPath;
    argv[3] = patchPath;
    int ret = bspatch(argc, argv);

    env->ReleaseStringUTFChars(oldPath_, oldPath);
    env->ReleaseStringUTFChars(newPath_, newPath);
    env->ReleaseStringUTFChars(patchPath_, patchPath);
    LOGD("native patch end");
    return ret;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_hbck_bspatchdiff_BsPatch_bsDiff(JNIEnv *env, jclass type, jstring oldPath_,
                                         jstring newPath_, jstring patchPath_) {
    LOGD("native diff begin");
    char *oldPath = (char *) env->GetStringUTFChars(oldPath_, 0);
    char *newPath = (char *) env->GetStringUTFChars(newPath_, 0);
    char *patchPath = (char *) env->GetStringUTFChars(patchPath_, 0);

    int argc = 4;
    char *argv[4];
    argv[0] = TAG;
    argv[1] = oldPath;
    argv[2] = newPath;
    argv[3] = patchPath;
    int ret = bsdiff(argc, argv);

    env->ReleaseStringUTFChars(oldPath_, oldPath);
    env->ReleaseStringUTFChars(newPath_, newPath);
    env->ReleaseStringUTFChars(patchPath_, patchPath);
    LOGD("native diff end");
    return ret;
}
  • 调用核心代码
 //合并差分包
    private fun doBsPatch() {

        Thread {
            val oldPath = Environment.getExternalStorageDirectory().absolutePath + File.separator + "old2.apk"
            val newPath = Environment.getExternalStorageDirectory().absolutePath + File.separator + "newPatch.apk"
            val patchPath = Environment.getExternalStorageDirectory().absolutePath + File.separator + "apkDiff.patch"
            val ret = BsPatch.bsPatch(oldPath, newPath, patchPath)
            runOnUiThread {
                if (ret < 0) {
                    Toast.makeText(this, "合并失败", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(this, "合并成功", Toast.LENGTH_SHORT).show()
                    this@MainActivity.installApk(newPath)
                }
            }
        }.start()
    }
源码下载地址:https://download.csdn.net/download/u014389362/10373005
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
力扣第二是一道链表相关的目,要求实现一个函数来将两个非负整数链表相加,并返回一个新的链表表示它们的和。 在解决这个问时,可以设立一个表示进位的变量carried,并建立一个新的链表来存储结果。然后,使用while循环来同时处理两个输入链表,每次取出对应的节点值相加,并将结果加上进位值carried后的值作为一个新节点加入新链表的后面。当遍历完两个链表后,如果最后还有进位,需要再添加一个节点来存储进位的值。最后返回新链表的头节点即可。 下面是使用Java语言编写的实现代码: ```java public class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } class Solution { public ListNode addTwoNumbers(ListNode l1, ListNode l2) { ListNode dummy = new ListNode(0); int sum = 0; ListNode cur = dummy; ListNode p1 = l1, p2 = l2; while (p1 != null || p2 != null) { if (p1 != null) { sum += p1.val; p1 = p1.next; } if (p2 != null) { sum += p2.val; p2 = p2.next; } cur.next = new ListNode(sum % 10); sum /= 10; cur = cur.next; } if (sum == 1) { cur.next = new ListNode(1); } return dummy.next; } } ``` 以上就是力扣两数相加第二Java实现代码。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [【Java版】LeetCode 力扣第 2 两数相加 (Add Two Numbers)](https://blog.csdn.net/monokai/article/details/108132843)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值