当前位置: 首页 > news >正文

android studio 3.0 Ndk 开发- 利用增量更新进行 apk的覆盖安装

学习目的

  • 在android studio 3.0的 基础上同时生成多个so包,cmake的编写
  • 开发安卓程序进行ndk开发 自动生成 增量包 patch
  • 生成 合并的so 库进行patch文件的合并
  • 解决 引用三方so库 方法报红 的问题

演示

增量更新就是,app自动更新的时候不会把整个新版本的apk包下载下来 进行覆盖安装,而是将一个 新版本和老版本 进行比较 生成一个 patch包,把patch包下载下来 和当前版本进行合并。然后在进行覆盖安装。

为了演示 我们先创建一个项目。将相同包名 的两个不同版本的apk 放到assert 目录下。然后将这两个包拷贝到 指定文件夹下。在生成一个patch 包。

下过如下

就这样 我们把assets目录下的两个apk。拷贝到了文件夹下,并生成了一个patch包。

这个patch包就是 app-new 和app-old 中的区别的部分。

然后我们可以吧这个patch包放到服务器。这样用户在使用app的时候 只用下载patch包,然后和当前app进行合并。就可以覆盖安装了。

下面我们在看一下 合并包的效果。我们还在该手机上。直接安装目录下的app-old.apk,然后点击合成按钮。会在线程里合成一个新的apk。然后进行覆盖安装。

我们看到了。我们的app 进行了覆盖安装。

  • 注意:
    • 这个叫oldtonew.apk 并不是这个文件夹下的 patch 和 app-old.apk合成的。因为用户使用的时候并不一定手机里会有 老版本的apk安装包。
    • 高于7.0 版本 的系统 覆盖安装会有一些坑。可以查看这篇文章。 https://juejin.im/post/5a7163866fb9a01ca3259710

包的拆分

需要的工具

  • bzip2 : http://www.bzip.org/downloads.html 下载放一边

  • bsdiff : https://github.com/mendsley/bsdiff clone 一波

    有用的就两个文件 。ok了。下载完这些 准备完事儿了。开始写代码了。

如何生成多个so文件(先了解一下)

如果androidstudio 3.0 上还不会使用ndk 开发 请看这篇文章 https://juejin.im/post/5a72bc53f265da3e364197c0 看完这个基本了解个大概了。androidstudio 已经不用 编写 .mk 文件了。

我们想创建一个项目。生成一个 diff.so 和patch.so 两个包。怎么做呢

这样一目了然了。再看看 这三个CmakeList.txt 怎么写的呢

diff的 CmakeList.txt

cmake_minimum_required(VERSION 3.4.1)


add_library( # Specifies the name of the library.
# 这里是你so的名字。刚才在 MainActivity里面要引用的
             diff

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
              #这里是刚才 创建的c++ 代码的名字
             Diff.c
            )

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

target_link_libraries( # Specifies the target library.
                       diff
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

patch 的 CmakeList.txt

cmake_minimum_required(VERSION 3.4.1)

# include_directories(
#             "${PROJECT_SOURCE_DIR}/..")
add_library( # Specifies the name of the library.
# 这里是你so的名字。刚才在 MainActivity里面要引用的
             patch

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
              #这里是刚才 创建的c++ 代码的名字
             PatchUtils.c
            )

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

target_link_libraries( # Specifies the target library.
                        patch
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

被gradle 引用的 CmakeList.txt

cmake_minimum_required(VERSION 3.4.1)

#添加子目录,将会调用子目录中的CMakeLists.txt
ADD_SUBDIRECTORY(diff)
ADD_SUBDIRECTORY(patch)

复制代码

在看一下 我们的gradle文件

请注意一下路径。 如果我们的代码写的没有问题。直接 build ->make project

可以看到 我们生成了 diff.so 和 patch.so 文件夹。 现在如果我们在创建什么项目 直接使用这两个so文件。就可以进行 拆分 和合并了。

拆分,合并代码编写。

拆分 和 合并 要使用 刚才 下载的 bsdiff 文件。bsdiff 文件又依赖于 bzip2 的库

因为拆分 与 合并 基本类似。就展示 拆分的代码了。我会把所有代码放到github上去。因为这两个库在使用的时候有特别多的坑。

先看一下目录结构吧

把刚才下载的bzip2 包放到 cpp目录下。 注意 不是全部都放过来。

在diff目录下新建一些 拆分需要的代码。

注意 如果你不知道 sven_com_apkpatchserver_DiffUtils.h 这个文件是如何生成的,你需要先看这篇文章,不然下面的看不懂了https://juejin.im/post/5a72bc53f265da3e364197c0

新建一个 DiffUtils 的工具类


public class DiffUtils {

    static {//导入libhello.so 文件。 这里面只写hello就可以
        System.loadLibrary("diff");
    }

    /**
     * native方法 比较路径为oldPath的apk与newPath的apk之间差异,并生成patch包,存储于patchPath
     * <p>
     * 返回:0,说明操作成功
     *
     * @param oldApkPath 示例:/sdcard/old.apk
     * @param newApkPath 示例:/sdcard/new.apk
     * @param patchPath  示例:/sdcard/xx.patch
     * @return
     */
    public static native String genDiff(String oldApkPath, String newApkPath, String patchPath);
}

复制代码

生成 他的 native 头文件 sven_com_apkpatchserver_DiffUtils.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class sven_com_apkpatchserver_DiffUtils */

#ifndef _Included_sven_com_apkpatchserver_DiffUtils
#define _Included_sven_com_apkpatchserver_DiffUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     sven_com_apkpatchserver_DiffUtils
 * Method:    genDiff
 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
 */
JNIEXPORT jstring JNICALL Java_sven_com_apkpatchserver_DiffUtils_genDiff
  (JNIEnv *, jclass, jstring, jstring, jstring);




#ifdef __cplusplus
}
#endif
#endif

复制代码

还记得 上文提到的github 中 有一个 bsdiff.c 的文件么 全部拷贝 放到diff文件夹下,这里更名为 diff.c 在这个代码的基础上进行编写 代码如下,特别多。请小心。下面全是c 语言的代码了。如果看不懂可以先从jni学起。

//
// Created by mypro on 2018/1/28.
//
/*-
 * Copyright 2003-2005 Colin Percival
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "sven_com_apkpatchserver_DiffUtils.h"


#include "../bzip2/bzlib.c"
#include "../bzip2/crctable.c"
#include "../bzip2/compress.c"
#include "../bzip2/decompress.c"
#include "../bzip2/randtable.c"
#include "../bzip2/blocksort.c"
#include "../bzip2/huffman.c"

#include <sys/types.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


#define MIN(x, y) (((x)<(y)) ? (x) : (y))

static void split(off_t *I, off_t *V, off_t start, off_t len, off_t h) {
    off_t i, j, k, x, tmp, jj, kk;

    if (len < 16) {
        for (k = start; k < start + len; k += j) {
            j = 1;
            x = V[I[k] + h];
            for (i = 1; k + i < start + len; i++) {
                if (V[I[k + i] + h] < x) {
                    x = V[I[k + i] + h];
                    j = 0;
                };
                if (V[I[k + i] + h] == x) {
                    tmp = I[k + j];
                    I[k + j] = I[k + i];
                    I[k + i] = tmp;
                    j++;
                };
            };
            for (i = 0; i < j; i++) V[I[k + i]] = k + j - 1;
            if (j == 1) I[k] = -1;
        };
        return;
    };

    x = V[I[start + len / 2] + h];
    jj = 0;
    kk = 0;
    for (i = start; i < start + len; i++) {
        if (V[I[i] + h] < x) jj++;
        if (V[I[i] + h] == x) kk++;
    };
    jj += start;
    kk += jj;

    i = start;
    j = 0;
    k = 0;
    while (i < jj) {
        if (V[I[i] + h] < x) {
            i++;
        } else if (V[I[i] + h] == x) {
            tmp = I[i];
            I[i] = I[jj + j];
            I[jj + j] = tmp;
            j++;
        } else {
            tmp = I[i];
            I[i] = I[kk + k];
            I[kk + k] = tmp;
            k++;
        };
    };

    while (jj + j < kk) {
        if (V[I[jj + j] + h] == x) {
            j++;
        } else {
            tmp = I[jj + j];
            I[jj + j] = I[kk + k];
            I[kk + k] = tmp;
            k++;
        };
    };

    if (jj > start) split(I, V, start, jj - start, h);

    for (i = 0; i < kk - jj; i++) V[I[jj + i]] = kk - 1;
    if (jj == kk - 1) I[jj] = -1;

    if (start + len > kk) split(I, V, kk, start + len - kk, h);
}

static void qsufsort(off_t *I, off_t *V, u_char *old, off_t oldsize) {
    off_t buckets[256];
    off_t i, h, len;

    for (i = 0; i < 256; i++) buckets[i] = 0;
    for (i = 0; i < oldsize; i++) buckets[old[i]]++;
    for (i = 1; i < 256; i++) buckets[i] += buckets[i - 1];
    for (i = 255; i > 0; i--) buckets[i] = buckets[i - 1];
    buckets[0] = 0;

    for (i = 0; i < oldsize; i++) I[++buckets[old[i]]] = i;
    I[0] = oldsize;
    for (i = 0; i < oldsize; i++) V[i] = buckets[old[i]];
    V[oldsize] = 0;
    for (i = 1; i < 256; i++) if (buckets[i] == buckets[i - 1] + 1) I[buckets[i]] = -1;
    I[0] = -1;

    for (h = 1; I[0] != -(oldsize + 1); h += h) {
        len = 0;
        for (i = 0; i < oldsize + 1;) {
            if (I[i] < 0) {
                len -= I[i];
                i -= I[i];
            } else {
                if (len) I[i - len] = -len;
                len = V[I[i]] + 1 - i;
                split(I, V, i, len, h);
                i += len;
                len = 0;
            };
        };
        if (len) I[i - len] = -len;
    };

    for (i = 0; i < oldsize + 1; i++) I[V[i]] = i;
}

static off_t matchlen(u_char *old, off_t oldsize, u_char *new, off_t newsize) {
    off_t i;

    for (i = 0; (i < oldsize) && (i < newsize); i++)
        if (old[i] != new[i]) break;

    return i;
}

static off_t search(off_t *I, u_char *old, off_t oldsize,
                    u_char *new, off_t newsize, off_t st, off_t en, off_t *pos) {
    off_t x, y;

    if (en - st < 2) {
        x = matchlen(old + I[st], oldsize - I[st], new, newsize);
        y = matchlen(old + I[en], oldsize - I[en], new, newsize);

        if (x > y) {
            *pos = I[st];
            return x;
        } else {
            *pos = I[en];
            return y;
        }
    };

    x = st + (en - st) / 2;
    if (memcmp(old + I[x], new, MIN(oldsize - I[x], newsize)) < 0) {
        return search(I, old, oldsize, new, newsize, x, en, pos);
    } else {
        return search(I, old, oldsize, new, newsize, st, x, pos);
    };
}

static void offtout(off_t x, u_char *buf) {
    off_t y;

    if (x < 0) y = -x; else y = x;

    buf[0] = y % 256;
    y -= buf[0];
    y = y / 256;
    buf[1] = y % 256;
    y -= buf[1];
    y = y / 256;
    buf[2] = y % 256;
    y -= buf[2];
    y = y / 256;
    buf[3] = y % 256;
    y -= buf[3];
    y = y / 256;
    buf[4] = y % 256;
    y -= buf[4];
    y = y / 256;
    buf[5] = y % 256;
    y -= buf[5];
    y = y / 256;
    buf[6] = y % 256;
    y -= buf[6];
    y = y / 256;
    buf[7] = y % 256;

    if (x < 0) buf[7] |= 0x80;
}

int bsdiff_main(int argc, char *argv[]) {
    int fd;
    u_char *old, *new;
    off_t oldsize, newsize;
    off_t *I, *V;
    off_t scan, pos, len;
    off_t lastscan, lastpos, lastoffset;
    off_t oldscore, scsc;
    off_t s, Sf, lenf, Sb, lenb;
    off_t overlap, Ss, lens;
    off_t i;
    off_t dblen, eblen;
    u_char *db, *eb;
    u_char buf[8];
    u_char header[32];
    FILE *pf;
    BZFILE *pfbz2;
    int bz2err;

    if (argc != 4) errx(1, "usage: %s oldfile newfile patchfile\n", argv[0]);

    /* Allocate oldsize+1 bytes instead of oldsize bytes to ensure
        that we never try to malloc(0) and get a NULL pointer */
    if (((fd = open(argv[1], O_RDONLY, 0)) < 0) ||
        ((oldsize = lseek(fd, 0, SEEK_END)) == -1) ||
        ((old = malloc(oldsize + 1)) == NULL) ||
        (lseek(fd, 0, SEEK_SET) != 0) ||
        (read(fd, old, oldsize) != oldsize) ||
        (close(fd) == -1))
        err(1, "%s", argv[1]);

    if (((I = malloc((oldsize + 1) * sizeof(off_t))) == NULL) ||
        ((V = malloc((oldsize + 1) * sizeof(off_t))) == NULL))
        err(1, NULL);

    qsufsort(I, V, old, oldsize);

    free(V);

    /* Allocate newsize+1 bytes instead of newsize bytes to ensure
        that we never try to malloc(0) and get a NULL pointer */
    if (((fd = open(argv[2], O_RDONLY, 0)) < 0) ||
        ((newsize = lseek(fd, 0, SEEK_END)) == -1) ||
        ((new = malloc(newsize + 1)) == NULL) ||
        (lseek(fd, 0, SEEK_SET) != 0) ||
        (read(fd, new, newsize) != newsize) ||
        (close(fd) == -1))
        err(1, "%s", argv[2]);

    if (((db = malloc(newsize + 1)) == NULL) ||
        ((eb = malloc(newsize + 1)) == NULL))
        err(1, NULL);
    dblen = 0;
    eblen = 0;

    /* Create the patch file */
    if ((pf = fopen(argv[3], "w")) == NULL)
        err(1, "%s", argv[3]);

    /* Header is
        0	8	 "BSDIFF40"
        8	8	length of bzip2ed ctrl block
        16	8	length of bzip2ed diff block
        24	8	length of new file */
    /* File is
        0	32	Header
        32	??	Bzip2ed ctrl block
        ??	??	Bzip2ed diff block
        ??	??	Bzip2ed extra block */
    memcpy(header, "BSDIFF40", 8);
    offtout(0, header + 8);
    offtout(0, header + 16);
    offtout(newsize, header + 24);
    if (fwrite(header, 32, 1, pf) != 1)
        err(1, "fwrite(%s)", argv[3]);

    /* Compute the differences, writing ctrl as we go */
    if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
        errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
    scan = 0;
    len = 0;
    lastscan = 0;
    lastpos = 0;
    lastoffset = 0;
    while (scan < newsize) {
        oldscore = 0;

        for (scsc = scan += len; scan < newsize; scan++) {
            len = search(I, old, oldsize, new + scan, newsize - scan,
                         0, oldsize, &pos);

            for (; scsc < scan + len; scsc++)
                if ((scsc + lastoffset < oldsize) &&
                    (old[scsc + lastoffset] == new[scsc]))
                    oldscore++;

            if (((len == oldscore) && (len != 0)) ||
                (len > oldscore + 8))
                break;

            if ((scan + lastoffset < oldsize) &&
                (old[scan + lastoffset] == new[scan]))
                oldscore--;
        };

        if ((len != oldscore) || (scan == newsize)) {
            s = 0;
            Sf = 0;
            lenf = 0;
            for (i = 0; (lastscan + i < scan) && (lastpos + i < oldsize);) {
                if (old[lastpos + i] == new[lastscan + i]) s++;
                i++;
                if (s * 2 - i > Sf * 2 - lenf) {
                    Sf = s;
                    lenf = i;
                };
            };

            lenb = 0;
            if (scan < newsize) {
                s = 0;
                Sb = 0;
                for (i = 1; (scan >= lastscan + i) && (pos >= i); i++) {
                    if (old[pos - i] == new[scan - i]) s++;
                    if (s * 2 - i > Sb * 2 - lenb) {
                        Sb = s;
                        lenb = i;
                    };
                };
            };

            if (lastscan + lenf > scan - lenb) {
                overlap = (lastscan + lenf) - (scan - lenb);
                s = 0;
                Ss = 0;
                lens = 0;
                for (i = 0; i < overlap; i++) {
                    if (new[lastscan + lenf - overlap + i] ==
                        old[lastpos + lenf - overlap + i])
                        s++;
                    if (new[scan - lenb + i] ==
                        old[pos - lenb + i])
                        s--;
                    if (s > Ss) {
                        Ss = s;
                        lens = i + 1;
                    };
                };

                lenf += lens - overlap;
                lenb -= lens;
            };

            for (i = 0; i < lenf; i++)
                db[dblen + i] = new[lastscan + i] - old[lastpos + i];
            for (i = 0; i < (scan - lenb) - (lastscan + lenf); i++)
                eb[eblen + i] = new[lastscan + lenf + i];

            dblen += lenf;
            eblen += (scan - lenb) - (lastscan + lenf);

            offtout(lenf, buf);
            BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
            if (bz2err != BZ_OK)
                errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);

            offtout((scan - lenb) - (lastscan + lenf), buf);
            BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
            if (bz2err != BZ_OK)
                errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);

            offtout((pos - lenb) - (lastpos + lenf), buf);
            BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
            if (bz2err != BZ_OK)
                errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);

            lastscan = scan - lenb;
            lastpos = pos - lenb;
            lastoffset = pos - scan;
        };
    };
    BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
    if (bz2err != BZ_OK)
        errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);

    /* Compute size of compressed ctrl data */
    if ((len = ftello(pf)) == -1)
        err(1, "ftello");
    offtout(len - 32, header + 8);

    /* Write compressed diff data */
    if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
        errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
    BZ2_bzWrite(&bz2err, pfbz2, db, dblen);
    if (bz2err != BZ_OK)
        errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
    BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
    if (bz2err != BZ_OK)
        errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);

    /* Compute size of compressed diff data */
    if ((newsize = ftello(pf)) == -1)
        err(1, "ftello");
    offtout(newsize - len, header + 16);

    /* Write compressed extra data */
    if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
        errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
    BZ2_bzWrite(&bz2err, pfbz2, eb, eblen);
    if (bz2err != BZ_OK)
        errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
    BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
    if (bz2err != BZ_OK)
        errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);

    /* Seek to the beginning, write the header, and close the file */
    if (fseeko(pf, 0, SEEK_SET))
        err(1, "fseeko");
    if (fwrite(header, 32, 1, pf) != 1)
        err(1, "fwrite(%s)", argv[3]);
    if (fclose(pf))
        err(1, "fclose");

    /* Free the memory we used */
    free(db);
    free(eb);
    free(I);
    free(old);
    free(new);

    return 0;
}

//JNI 调用
JNIEXPORT jstring JNICALL Java_sven_com_apkpatchserver_DiffUtils_genDiff
        (JNIEnv *env, jclass jcls, jstring oldfile_jstr, jstring newfile_jstr,
         jstring patchfile_jstr) {
    int argc = 4;
    char *oldfile = (char *) (*env)->GetStringUTFChars(env, oldfile_jstr, NULL);
    char *newfile = (char *) (*env)->GetStringUTFChars(env, newfile_jstr, NULL);
    char *patchfile = (char *) (*env)->GetStringUTFChars(env, patchfile_jstr, NULL);

//参数(第一个参数无效)
    char *argv[4];
    argv[0] = "bsdiff";
    argv[1] = oldfile;
    argv[2] = newfile;
    argv[3] = patchfile;
//这个方法是和新方法,他会根据 old.apk new.apk 进行拆分,然后生成 patch 放到指定文件夹下
    bsdiff_main(argc, argv);

    (*env)->ReleaseStringUTFChars(env, oldfile_jstr, oldfile);
    (*env)->ReleaseStringUTFChars(env, newfile_jstr, newfile);
    (*env)->ReleaseStringUTFChars(env, patchfile_jstr, patchfile);

    return (*env)->NewStringUTF(env, "hello hello");
}

//void main(int argc, char **argv) {
//
//}

复制代码

到这里 拆分就完成了。 只需要在java代码里执行如下代码就行


        findViewById(R.id.diff).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String s = DiffUtils.genDiff(Constants.OLD_APK_PATH, Constants.NEW_APK_PATH, Constants.PATCH_FILE);
            }
        });

Constants.OLD_APK_PATH: old.apk 路径
Constants.NEW_APK_PATH: new.apk 路径
Constants.PATCH_FILE:   生成的patch 路径
复制代码

到这里 拆分 就都讲完了。合并 的代码 大同小异这里就不赘述了。我会把代码放到github上去。

使用patch.so 进行apk 与patch的合并。

新建一个项目,假设这个项目就是我们公司的app了。把刚才生成好的 patch.so文件 拿过来 复制到这里

然后我们开始编写 PatchUtils的代码了

注意

  • 我们如果想完美的使用so文件。我们带么的 名字 和路径 都要和 生成so 项目的代码的路径一致
    不然的话 代码 引用上就会有问题。 这就是为什么我们引用三方so文件 无法运行或者 写的代码报红了

比如这样

引用成功的话。基本就完成了。 我们只需要在代码里 简单调用就可以了


    class ApkUpdateTask extends AsyncTask<Void, Void, Boolean> {

        @Override
        protected Boolean doInBackground(Void... params) {
            try {
                //1.下载差分包
                String oldfile = ApkUtils.getSourceApkPath(MainActivity.this, getPackageName());
                //2.合并得到最新版本的APK文件
                String newfile = Constants.OLD_2_NEW_APK;
                String patchfile = Constants.PATCH_FILE;
                PatchUtils.patch(oldfile, newfile, patchfile);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            return true;
        }
        @Override
        protected void onPostExecute(Boolean result) {
            super.onPostExecute(result);

            //3.安装
            if (result) {
                Toast.makeText(MainActivity.this, "您正在进行无流量更新", Toast.LENGTH_SHORT).show();
                ApkUtils.installApk(MainActivity.this, Constants.OLD_2_NEW_APK);
            }

        }

    }

复制代码

到这里 我们就完成了apk 的 增量更新

覆盖安装的一些坑

  • 增量更新,在手机上安装的 apk。一定要是 release key 打包的,并且 v1 v2 对勾都要选中
  • 覆盖安装 要适配7.0 https://juejin.im/post/5a72bc53f265da3e364197c0
  • 覆盖安装不要忘记 这句代码
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

复制代码
  • 如果怎么都不行,可以查看 你的apk包是否有读写权限 可以这样

            String[] command = {"chmod", "777", Constants.OLD_2_NEW_APK};
            ProcessBuilder builder = new ProcessBuilder(command);
            try {
                builder.start();
            } catch (IOException e) {
                e.printStackTrace();
            }


复制代码

不懂得可以查看代码,最直观 https://github.com/wanghao200906/NdkBsDiffPractise

相关文章:

  • Teamviewer原理和阻止方法
  • 【BIEE】11_根据显示指标展示不同报表
  • 流程(上)
  • 好领导:提升领导威信力的110个管理奥秘
  • 我的重构第二步
  • 部署eolinker开源版接口管理
  • 基于django的生成二维码的接口
  • 09-移动端开发教程-Sass入门
  • while循环按行读文件的方式总结
  • ElasticSearch「1」本地安裝Elasticsearch 6.0.1 + Elasticsearch-head插件
  • 2018/02/09
  • PhysicsBasedAnimation学习
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • Mybatis分页插件PageHelper简单使用
  • 企业IM-2技术选型
  • es6--symbol
  • happypack两次报错的问题
  • httpie使用详解
  • Java小白进阶笔记(3)-初级面向对象
  • Linux Process Manage
  • node.js
  • Redux系列x:源码分析
  • Service Worker
  • Swift 中的尾递归和蹦床
  • ViewService——一种保证客户端与服务端同步的方法
  • vue-router的history模式发布配置
  • 程序员最讨厌的9句话,你可有补充?
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 聊聊flink的TableFactory
  • 每天一个设计模式之命令模式
  • 区块链将重新定义世界
  • 我有几个粽子,和一个故事
  • 移动端唤起键盘时取消position:fixed定位
  • 用jQuery怎么做到前后端分离
  • 正则学习笔记
  • ​VRRP 虚拟路由冗余协议(华为)
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • (阿里云万网)-域名注册购买实名流程
  • (二十四)Flask之flask-session组件
  • (附表设计)不是我吹!超级全面的权限系统设计方案面世了
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (三)mysql_MYSQL(三)
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • (转)h264中avc和flv数据的解析
  • (转)ObjectiveC 深浅拷贝学习
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .Net接口调试与案例
  • @WebService和@WebMethod注解的用法
  • [8481302]博弈论 斯坦福game theory stanford week 1
  • [Android]RecyclerView添加HeaderView出现宽度问题
  • [C#]科学计数法(scientific notation)显示为正常数字
  • [FFmpeg学习]从视频中获取图片
  • [Go WebSocket] 多房间的聊天室(三)自动清理无人房间