文档章节

NDK开发基础④增量更新之客户端合并差分包

逝我
 逝我
发布于 2016/09/22 10:40
字数 949
阅读 23
收藏 0

接续上篇NDK开发基础③增量更新之服务器端生成差分包

前情提要

增量更新原理就是在服务器端使用bsdiff进行文件内容比较,再使用了bzip2进行文件压缩 , 在下载APP时可以减少用户流量 。在客户端 , 则是将下载好的拆分包与现有的APK进行文件合并 , 得出新的APK, 再进行安装 。

生产资源及工具

bsdiff --- bsdiff 生成差分包及合并差分包库 , 使用bspatch.c文件 bzip2 --- bzip2 bsdiff 依赖 服务器 --- Tomcat 7.0 (模拟网络环境)放置差分包 , 供APP下载 开发工具 --- Android Studio 2.2RC2 NDK开发

一 , 合并差分包

Ⅰ 提取bzip2中的源文件

bzip2

Ⅱ 将bzip2加入到Android Studio项目中

首先将工程切换到Project模式 , 将bzip2文件夹复制到cpp目录下 。因为最新的Android Studio采用的是CMake构建工具 , 所有需要在bzip2目录下,创建一个CMakeLists.txt文件:

bzip2 cmake

Ⅲ 将bspatch.c复制到cpp目录下 , 并将自动生成的CMakeList.txt文件拖拽到cpp目录下 , 并添加子目录参与编译 。

bspatch cmake

修改了CMakeLists.txt文件的路径之后 , 需要在build.gradle中修改一下配置了:

build.gradle

并且配置一下build环境

build

Ⅳ 编写JNI

public class BspatchJNI {

    /**
     * 合并增量文件
     * @param oldFilePath 当前APK路径
     * @param newFilePath 合成后的新的APK路径
     * @param patchFilePath 增量文件路径
     */
    public static native void bspatchJNI(String oldFilePath,String newFilePath,String patchFilePath) ;

    static {
        System.loadLibrary("bspatch");
    }
}

Ⅴ 编写C函数 , 怎样找执行函数 , 上一篇已经说了 , 套路都是一样的 。

/*合并APK*/
JNIEXPORT void JNICALL
Java_com_zeno_incrementupdate_ndk_BspatchJNI_bspatchJNI(JNIEnv *env, jclass type,
														jstring oldFilePath_, jstring newFilePath_,
														jstring patchFilePath_) {
	const char *oldFilePath = (*env)->GetStringUTFChars(env, oldFilePath_, 0);
	const char *newFilePath = (*env)->GetStringUTFChars(env, newFilePath_, 0);
	const char *patchFilePath = (*env)->GetStringUTFChars(env, patchFilePath_, 0);


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

	int argc = 4 ;
	char* argv[4] ;
	argv[0] = "bspatch";
	argv[1] = oldFilePath;
	argv[2] = newFilePath;
	argv[3] = patchFilePath;

	bspatch_main(argc,argv);

	LOGE("MainActivity","%s","合并APK完成");

	(*env)->ReleaseStringUTFChars(env, oldFilePath_, oldFilePath);
	(*env)->ReleaseStringUTFChars(env, newFilePath_, newFilePath);
	(*env)->ReleaseStringUTFChars(env, patchFilePath_, patchFilePath);
}

需要注意的时 , 在bspatch.c中是需要引入bzip2的 , 所有需要在文件头部, 引入bzip2 :

// bzip2
#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"

#define LOGE(TAG,FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,TAG,FORMAT,__VA_ARGS__)

Ⅵ 使用

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

        @Override
        protected Boolean doInBackground(Void... params) {
            try {
                //1.下载差分包
                Log.e(TAG, "doInBackground: 正在下载。。。。" );
                File patchFile = DownloadUtils.download(Constants.URL_PATCH_DOWNLOAD);

                //获取当前应用的apk文件/data/app/app
                String oldFile = APKUtils.getSourceApkPath(MainActivity.this, getPackageName());
                //2.合并得到最新版本的APK文件
                String newApkPath = Constants.NEW_APK_PATH;
                String patchFileAbsolutePath = patchFile.getAbsolutePath();
                BspatchJNI.bspatchJNI(oldFile, newApkPath, patchFileAbsolutePath);

                Log.d(TAG, "oldfile:"+oldFile);
                Log.d(TAG, "newfile:"+newApkPath);
                Log.d(TAG, "patchfile:"+patchFileAbsolutePath);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }

            return true;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            progressDialog = new ProgressDialog(MainActivity.this);
            progressDialog.setTitle("正在下载...");
            progressDialog.show();
        }

        @Override
        protected void onPostExecute(Boolean result) {
            super.onPostExecute(result);
            progressDialog.dismiss();
            //3.安装
            if(result){
                Toast.makeText(MainActivity.this, "您正在进行无流量更新", Toast.LENGTH_SHORT).show();
                APKUtils.installApk(MainActivity.this, Constants.NEW_APK_PATH);
            }
        }

    }

使用起来都比较简单 , 这里就不将代码贴全了,篇末会给出github地址。

Ⅶ 打包

因为Android Studio使用了instant run技术 , 所以使用Android Studio生成APK最好是打正式包 , 并且包中内容要有差异性 , 然后再生成差分包 , 直接放置在WEB项目的WebContent根目录下即可 。

结语

增量更新 , 从服务器端到客户端实现 , 要写的代码其实不多 , 关键在于使用第三方C/C++源码的套路 , 使用JNI技术调用C/C++函数 , 其关键点就是找执行函数,通常为main函数 。NDK开发基础 , 这一篇算是结尾 , 新版的Android Studio的NDK支持比较完善 , 使用了CMake进行项目构建 ,语法高亮以及语法提示 , 都做得相当的好了 。开始下一个系列 , C++开发 。

###源码 IncrementUpdate

###参考 CMake Practice 百度网盘 密码: 58a3

© 著作权归作者所有

逝我
粉丝 4
博文 20
码字总数 31426
作品 0
长沙
程序员
私信 提问
开源 Android App 增量更新库 版本升级

开源 Android App 增量更新库 版本升级 经过几天的重构,我将之前写的一个Android 应用增量更新的示例程序重构为了一个开源库,现在已经push 到 GitHub 上,欢迎大家Watch、Star、Fork。 包含...

Cundong
2014/09/02
3.7K
2
Android NDK开发之旅 目录

Android NDK开发之旅 目录 Android NDK开发之旅1--NDK介绍 Android NDK开发之旅2--C语言--基本数据类型 Android NDK开发之旅3--C语言--指针 Android NDK开发之旅4--C语言--动态内存分配 Andr...

香沙小熊
2017/12/31
0
0
Android系统Recovery工作原理之使用update.zip升级过程分析(二)---u...

Android系统Recovery工作原理之使用update.zip升级过程分析(二)---update.zip差分包问题的解决 在上一篇末尾提到的生成差分包时出现的问题,现已解决,由于最近比较忙,相隔的时间也比较长...

lxp198837
2012/06/26
1K
0
Android Apk差分与合成更新

Android增量更新的原理是使用比较2个apk,然后通过差异与手机apk程序合成一个新的apk。 我们知道,获取手机端app中的app可以通过如下方法,类似常用的插件化读取第三方app资源的方式。 方法:...

IamOkay
2016/06/20
451
0
Android OpenGL开发目录

Android OpenGL开发目录 Android OpenGL开发1--VS2017+OpenGL环境的配置 to be continued... 其它目录 Android NDK开发之旅 目录 Android NDK开发之旅1--NDK介绍 Android NDK开发之旅2--C语言...

香沙小熊
2018/01/07
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Android面试常客之Handler全解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/fnhfire_7030/article/details/79518819 前言:又到了一年...

shzwork
22分钟前
4
0
position sticky 定位

本文转载于:专业的前端网站➫position sticky 定位 1、兼容性 https://caniuse.com/#search=sticky chrome、ios和firefox兼容性良好。 2、使用场景 sticky:粘性。粘性布局。 在屏幕范围内时...

前端老手
29分钟前
4
0
CentOS 7 yum 安装 PHP7.3 教程

参考:https://www.mf8.biz/centos-rhel-install-php7-3/ 1、首先安装 EPEL 源: yum install epel-release 安装 REMI 源: yum install http://rpms.remirepo.net/enterprise/remi-release......

dragon_tech
44分钟前
4
0
Linux物理网卡聚合及桥接

Linux内部实现的bridge可以把一台机器上的多张网卡桥接起来,从而把自己作为一台交换机。同时,LInux bridge还支持虚拟端口,即桥接的不一定都是物理网卡接口,还可以是虚拟接口。目前主要表...

xiangyunyan
44分钟前
4
0
一起来学Java8(一)——函数式编程

在这篇文章中,我们将了解到在Java8下如何进行函数式编程。 函数式编程 所谓的函数式编程就是把函数名字当做值进行传递,然后接收方拿到这个函数名进行调用。 首先来看下JavaScript如何进行函...

猿敲月下码
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部