随着社会的发展,虽然对于一个用户而言,流量不值钱,每个用户的手机会有多少个G的流量,或者不限制流量,或者经常使用wifi。所以目前在Android端流量的优化,可能没有那么重要了。但是当我们的用户更新一个app的时候,每次都需要下载一个完整的app有可能要等几分钟,像王者荣耀这样大型的游戏app,下载完整的app至少几百兆,但是我们新的补丁包有可能只有几十兆,或者几兆,这样让用户使用新的功能,需要去下载整个新的app不合理,因此,增量更新就派上用处了额,增量更新可以减少服务器的压力。在很多应用市场上,比如我使用的小米应用市场,它就自己集成了bsdiff,在某些集成了bspatch的客户端,比如抖音,微信...它会自动使用增量更新。

Bsdiff

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

我这里使用的是centos来下载编译bsdiff的,下载解压bsdiff之后,我们可以看到解压之后的文件夹里面有Makefile文件,但是当我们

在bsdiff文件夹下执行make命令 发现有如下报错:

Makefile:13: *** missing separator.  Stop.

第13行报错,可以打开Makefile看看:

这是因为在第13行 .ifndef 语句之前缺少Tab键的空格,下面的.endif也是如此,加入Tab空格之后,重新执行make命令,有可能会提示你缺少bzlib.h的头文件,其实在bsdiff官网上面也提到了,使用bsdiff和bspatch需要用到bzip2,所有需要安装bzip2。在终端执行下面命令,来查找bzip2需要的安装包 yum search bzip2 可以得到下面的提示,然后选择安装你需要的就OK了

然后使用下面的命令安装(-y 代表下面的安装步骤都是选择yes):

yum install -y bzip2-devel.x86_64

安装好bzip2之后再执行make命令,就可以编译得到相关bsdiff和bspatch文件了,执行ls命令可以看到两个绿色的可执行文件

使用以下命令可以得到bsdiff(同理bspatch也可以这样查看)的用法

可以看到bsdiff 需要传递三个参数,原文件  新的文件,差分文件,

Android项目当中的开发

在Android中,我们其实只需要将差分包 和 老版本的包进行合并就OK,因此我们使用到的是bspatch。差分工作是由服务器来完成(或者客户端完成之后上传到服务器,这个时候可以用脚本+工具结合使用)。那么下面进行客户端的开发。

使用Android Studio新建Native C++的项目,然后将bspatch.c拷贝到cpp目录下,之前编译的时候说过,bsdiff和bspatch依赖bzip2,打开bspatch.c其实也可以发现它引入了  #include <bzlib.h>  的头文件,所以这里我们也需要在项目当中引入bzip2的相关源代码,其实bzip2在我们的Android的源代码中已经存在,参见链接 。点开可以看到这里就是bzip2的源代码,里面有很多,但其实我们可以不用全部(当然也可以全部)拷贝到我们的代码中。可以在bzip2官网上下载最新的bzip2相关文件,我这里选择从bzip2的官网上下载,下载完成之后,我们看看Makefile文件:

这些 *.o文件其实就是我们需要生成的目标文件。这里那我们就将对应的*.c文件还有相关的依赖的*.h文件拷贝到我们的项目中就OK。我们新建一个bzip2的文件夹,将这些文件拷贝进去,最后如下图所示:

拷贝好之后就需要编写我们的CMakeLists.txt文件了,最后CMakeLists.txt的配置文件如下:

cmake_minimum_required(VERSION 3.4.1)
#引入头文件
include_directories(${CMAKE_SOURCE_DIR}/bzip2)
#批量导入对应的c文件
aux_source_directory(${CMAKE_SOURCE_DIR}/bzip2/ bzlibsrc)
add_library( native-libSHAREDnative-lib.cppbspatch.c${bzlibsrc})
target_link_libraries( native-liblog)

写好CMakeLists.txt并且编译通过之后,就可以写Android端的java的代码了。

Android端:

首先增加相关的权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

这里使用将差分包放在sdcard的形式来模拟网络下载,所以有sdcard的读写权限,还有android高版本需要的安装apk的权限。

我们知道在Android7.0之后,通过FileProvider来实现应用之间共享文件:

 <!--${applicationId}--><providerandroid:name="android.support.v4.content.FileProvider"android:authorities="com.android.bsdiff.demo.fileProvider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths" /></provider>

上面的android:authorities后面的value值 一般都是你的包名称+fileProvider(也可以不是fileProvider,是其他字符串),在我测试的时候我使用android:authorities = "${applicationId}.provider" 这样动态去配置好像 增量更新的时候 一直出现安装包解析错误,所以我这里就写死了包名+fileProvider的形式。

在meta-data中设置访问的权限:

<?xml version="1.0" encoding="utf-8"?>
<paths><external-path name="path" path="." />
</paths>

具体的FileProvider可以参看网上很多博客,或者google的官方文档。

具体的操作逻辑在 doPatchNative 方法的实现中,该方法传递3个参数,原文件(老版本),新版本文件,差分文件,最后原文件+差分文件合并成 新版本文件,我们对新版本文件进行安装操作:

public class MainActivity extends AppCompatActivity {static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView tv = findViewById(R.id.tv_version);tv.setText("当前版本:" + BuildConfig.VERSION_NAME);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {String[] perms = {Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.REQUEST_INSTALL_PACKAGES};if (checkSelfPermission(perms[0]) == PackageManager.PERMISSION_DENIED || checkSelfPermission(perms[1]) == PackageManager.PERMISSION_DENIED) {requestPermissions(perms, 200);}}}public void onUpdate(View view) {//网络请求下载查分包(省略。直接拷贝查分包到sd卡)new AsyncTask<Void, Void, File>() {@Overrideprotected File doInBackground(Void... voids) {//bspatch 做合成 得到新版本的apk文件//sz: linux>windows//rz: windows>linuxString patch = new File(Environment.getExternalStorageDirectory(),"patch.diff").getAbsolutePath();File newApk = new File(Environment.getExternalStorageDirectory(), "new.apk");if (!newApk.exists()) {try {newApk.createNewFile();} catch (IOException e) {e.printStackTrace();}}String oldApk = getApplicationInfo().sourceDir;doPatchNative(oldApk, newApk.getAbsolutePath(), patch);return newApk;}@Overrideprotected void onPostExecute(File apkFile) {//安装if (!apkFile.exists()) {Log.e("TAG","new file not exist");return;}Intent intent = new Intent(Intent.ACTION_VIEW);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);if (Build.VERSION.SDK_INT >= 24) { //Android 7.0及以上// 参数2 清单文件中provider节点里面的authorities ; 参数3  共享的文件,即apk包的file类Uri apkUri = FileProvider.getUriForFile(MainActivity.this,"com.android.bsdiff.demo.fileProvider", apkFile);//对目标应用临时授权该Uri所代表的文件intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.setDataAndType(apkUri, "application/vnd.android.package-archive");} else {intent.setDataAndType(Uri.fromFile(apkFile),"application/vnd.android.package-archive");}startActivity(intent);}}.execute();}private native void doPatchNative(String oldApk, String newApk, String patch);}

看native_lib中关于文件合并的调用:

extern "C"{extern int bspatch_main(int argc,char * argv[]);  //函数的申明
}extern "C"
JNIEXPORT void JNICALL
Java_com_android_bsdiff_demo_MainActivity_doPatchNative(JNIEnv *env, jobject instance,jstring oldApk_, jstring newApk_,jstring patch_) {const char *oldApk = env->GetStringUTFChars(oldApk_, 0);const char *newApk = env->GetStringUTFChars(newApk_, 0);const char *patch = env->GetStringUTFChars(patch_, 0);char *argv[] = {"bspatch",const_cast<char *>(oldApk),const_cast<char *>(newApk),const_cast<char *>(patch)};bspatch_main(4,argv);env->ReleaseStringUTFChars(oldApk_, oldApk);env->ReleaseStringUTFChars(newApk_, newApk);env->ReleaseStringUTFChars(patch_, patch);
}

在bspatch.c文件中,其实原本作者定义合成文件的函数名称为main,如下:

作为bsdiff的设计者将合成文件的方法名称命名为main,这个无可厚非,但是我们的项目中,可能用到很多个第三方的c文件,有可能作者设计入口函数都是main,所以在调用的时候我们都调用main读起来有点不清晰,所以在此就将main方法修改一个名称(这也是得到源代码的好处,可以随意修改)

看见标记红色的部分,其实就是上文在centos中调用./bspatch --help时候的提示语,这也反映这个函数就是合成时候调用的函数。

在native-lib.cpp需要调用bspatch.c里面的函数,但是一个是.cpp一个是.c文件,所以在调用.c文件的时候,我们需要在extern “C”中进行声明。如下:

extern "C"{extern int bspatch_main(int argc,char * argv[]);  //函数的申明
}

这样我们声明了一个名称为bspatch_main的函数,但是这个函数在外部实现,在Android Studio中

我们其实可以点击红色框框的地方,看看这个函数的实现在什么地方,其实最后定位到的就是bspatch中的这个方法,这样,我们就可以在native-lib中去调用bspatch_main这个方法了。

在本文的Demo中,我们的old.apk和new.apk的区别就是下面

就是版本号的区别,将new.apk中的versionCode修改为 2 ,versionName修改为 "2.0",那么就需要使用上面编译生成的bsdiff(绿色的)可执行文件来生成差分文件。

在linux系统中,可以使用rz,或者sz  向linux服务器上传文件,或者从linux服务器下载文件,但是使用这个命令必须是安装了终端,像windows中xshell, mac系统(自带的终端不行)中的FinalShell才能使用。将编译出的old.apk,new.apk上传上去后,调用

./bsdiff old.apk new.apk patch.diff

之后就可以生成差分文件patch.diff,然后将差分文件上传到服务器,就可以供用户下载 合成,并且安装咯。在Demo中,我将打包后的old.apk  new.apk patch.diff放在assets文件夹下,下载之后,可以将对应的patch.diff拷贝到sdk,然后安装old.apk,就可以体验增量更新了。

问题:在Android,我们可能有很多个应用市场,或者很多个推广渠道,每次Android打线上包的时候,都需要打上百个android的apk包,这样,就导致我们做增量更新的时候,不可能每次都对这个100多个渠道进行增量更新,就算是100多个渠道好实现, 那么某个渠道可能有很多个版本的用户都在使用,这时候,需要对某个渠道的某个版本做差分包,这样就有点麻烦了。我们可以选择某个渠道,比如官网渠道,或者看看网上”多渠道包的增量更新“怎么做的。

Demo传送门

Android 增量更新相关推荐

  1. android cpp做成so库,Android增量更新(二)—制作合成文件so库

    ###前言 在上节中,我们已经学习了`Android增量更新`的实现原理,那么在Android开发的移动端,我们需要涉及到的其实是`文件合成`的这个流程. 那么,今天就来讲讲增量更新合成流程的实现吧. ...

  2. Android 增量更新实现

    如果app依赖本地更新,更新比较频繁的话,每次更新都要让用户下载完整安装包,用户体验会比较差.目前,很多应用商店都实现了apk的增量更新 正常apk更新逻辑: 打包V1.0版本,大小30M,用户安装完 ...

  3. Android增量更新框架

    Android增量更新框架 框架介绍 功能简介 简易效果图 增量更新配置 快速使用 Api详解 项目地址 框架介绍 功能简介 Android App更新框架,包含增量更新.多线程下载等功能.一句代码链 ...

  4. android中热更新模式,热更新再牛,也少不了Android 增量更新

    码个蛋(codeegg)第 785 次推文 作者: C_YQ 原文: https://juejin.im/post/5da845cdf265da5b7244c63e 码妞看世界 接骨草~ 虽然没有增量 ...

  5. Android 增量更新实例(Smart App Updates)

    http://892848153.iteye.com/blog/2022851 转自:http://my.oschina.net/liucundong/blog/160436 目录[-] 官方说明 实 ...

  6. Android增量更新——bsdiffbspatch

    原址 现在公司对于app增量更新的需求越来越多,因为增量更新不仅仅可以节省流量,更重要的一点是减少用户更新app的时间,有益于用户体验.    这篇文章将介绍运用patch差分包的形式实现增量更新.为 ...

  7. android增量更新详细解读

    1 增量更新的意义 a 神马是增量更新? 这个概念很早就被提出,但是目前的形式,小厂用的很少,大厂用的比较多:企鹅的QQ和微信里面都有增量更新着玩意:到底神马是增量更新? 首先增量更新主要用于新版本a ...

  8. Android 增量更新完全解析 是增量不是热修复

    本文在我的微信公众号:鸿洋(hongyangAndroid)首发. 转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/5276165 ...

  9. android差分升级原理,BigNews Android 增量更新框架差分包升级 @codeKK c开源站

    支持增量包/差分包/升级包 原理:在服务器端使用 bsdiff 工具将新老安装包的差异打包为一个体积较小的差分包/升级包,然后在 App 端通过 bspatch 工具(和 bsdiff 配套的)用差分 ...

最新文章

  1. 联想输入快捷键_UG软件F8快捷键和笔记本电脑F8快捷键冲突的解决方法
  2. 基于MVC4+EasyUI的Web开发框架经验总结(8)--实现Office文档的预览
  3. 如何使wordpress导航栏在新窗口打开
  4. 单片机学习应用六大重要部分
  5. Apache HBase快照介绍
  6. Eigen教程(8)
  7. typical career path for consulting industry
  8. mysql异地备份_MySQL数据库异地备份与还原方法
  9. 2009年岁末年总结
  10. android 设置视频音量大小,为cocos2d-x添加调节视频音量的功能(Android)
  11. 信息学奥赛一本通 2052:【例3.2】范围判断
  12. win安装MinGW-w64
  13. JNA参数传递问题,Java数组
  14. Jmeter+Grafana+InfluxDB
  15. mappedBy作用
  16. 概率论与数理统计公式整理
  17. Linux 误删文件后恢复文件
  18. java 去掉图片水印文字_Java实现图片水印工具类
  19. 数据库防火墙应具备哪些能力
  20. 架构搜索文献笔记(9):《CurveLane-NAS: Unifying Lane-Sensitive Architecture Search and Adaptive Point》

热门文章

  1. 编程软件IAR安装使用及程序下载
  2. 374. 导弹防御塔
  3. SAP SD MM PP HR FI CO EWM 等PA认证考试报名/指南/题库
  4. JAVA_02 j2se j2ee j2me jse jee jme
  5. Vim指令对应的英语全称
  6. 全景分割相关论文写作与准备笔记
  7. 南阳oj 语言入门 房间安排
  8. idea,eclipse中注解使用的计算机用户名修改
  9. c语言课程农夫过河问题实验心得,农夫过河实验报告
  10. yocto运行时依赖规则