价值100W的经验分享: 基于JSPatch的iOS应用线上Bug的即时修复方案,附源码.
限于iOS AppStore的审核机制,一些新的功能的添加或者bug的修复,想做些节日专属的活动等,几乎都是不太可能的.从已有的经验来看,也是有了一些比较常用的解决方案.本文先是会简单说明对比大部分方案,然后会注重阐述基于JSPatch的在线更新机制的设计和实现.对于任何一家有一定用户基础的iOS应用来说,在线更新技术所产生的直接和间接价值都将远远超过100W.理解,并掌握它;实在没有时间,就记住它,因为这篇文章不仅仅是讨论.
实例地址:https://github.com/ios122/ios122
几种在线更新方案的对比: 为什么是JSPatch?
方案一: 申请"加急审核"
- 方法: 提交应用时,选择"加急审核".
- 优点: 操作简单,只需要重新上传应用即可;
- 缺点:"加急审核",肯定是不能经常使用的.
- 简评: 我想,这可能是大多数公司遇到紧急问题时,最常使用的方案.一个应用,每年是有若干次机会申请"加急审核",来缩短应用新版本的审核周期.通常审核周期是7天左右;"加急审核",通常只需要3天左右.
方案二: 使用 webview + Html5 页面
- 方法: 特定的可能需要经常换的页面使用WebView来显示,内部使用Html5的内容来填充.当需要改变页面时,只需要改变下服务器接口返回的内容即可.
- 优点: 对于内容的更新,足够灵活和迅速.
- 缺点: 无法修复非HTML5页面的Bug;Html5 交互和UI通常逊色于原生页面.
- 简评: 混合应用常用的方式,如PhoneGap等;对于大多数原生应用来说,此方案基本无适用性.
方案三: 编写基于ReactNative的应用
- 方法: 使用 ReactNative 来编写应用或应用的部分页面,更多介绍参见: React Native 官方文档中文版
- 优点: 原生UI,原生交互,支持服务器方式在线更新应用.
- 缺点: 对于非ReactNative编写的页面无能为力.
- 简评: 个人主观是很看好 ReactNative的,也在慢慢踩坑;但现实是大部分公司的已有项目是基于Objetive-C的,所以基于ReactNative的在线更新策略,目前对于大多说公司来说也并不具有可行性.
方案四: 基于JSPatch实现在线补丁式更新
- 方法: 在自己的项目中引入JSPatch库,然后参见下文继续讨论的方案细节实施即可.JSPatch的入门使用,参见: http://www.ios122.com/2015/11/jspatch/
- 优点: 支持操作所有工程中引入的CocoaTouch库与各种第三方库.可完全自由定义与重写已有代码的逻辑.
- 缺点: JS语法操作API,语法转换有一定成本.
- 简评: 大多数时候,我们需要的只是重写下某个方法,甚至某个判断,某个默认值,就可以很好地修复某个线上的Bug.所以,JSPatch,已经够用了.当然,如果是对于复杂的新功能的添加的话,建议还是提交审核吧.另外,不得不说一句,JSPatch + ReactNatvie 将来或许会成为一个很强力的组合,前者侧重于Bug的修复,后者侧重于复杂新需求的添加.本文接下来的篇幅将注重讨论基于JSPatch的线上Bug的即时修复方案.
关于使用JSPatch几个技术点的分析与实现.
基本实现原理
安装本地所有补丁 --> 联网更新补丁信息,并安装有更新或新增加的补丁.注意此处的安装,指的是执行以下JS文件中的代码.此段代码会替换某个类的默认实现.当App运行到需要某个类的某个被JSPatch替换的方法时,会走JS定义的逻辑,而不再是源代码中默认的逻辑.可以看下DEMO.另外,我们的应用和示例中都使用了Objection这个依赖注入的库,你可能也要先温习下: [Objection,一个轻量级的Objective-C依赖注入框架
](http://www.ios122.com/2015/11/objection/)
文件 md5 值的获取与校验
mac上,获取某个文件的md5值,直接在终端输入命令:
md5 文件完整路径.
关于校验md5的代码,其实最核心的是如何在oc中使用代码获取某个文件的md5值,然后进行比对.网上的示例很多,但可能不太靠谱,下面贴一段确实可行的,注意要引入系统库 #include <CommonCrypto/CommonDigest.h>
:
/**
* 获取文件的md5信息.
*
* @param path 文件路径.
*
* @return 文件的md5值.
*/
-(NSString *)mcMd5HashOfPath:(NSString *)path
{
NSFileManager *fileManager = [NSFileManager defaultManager];
// 确保文件存在.
if( [fileManager fileExistsAtPath:path isDirectory:nil] )
{
NSData *data = [NSData dataWithContentsOfFile:path];
unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5( data.bytes, (CC_LONG)data.length, digest );
NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for( int i = 0; i < CC_MD5_DIGEST_LENGTH; i++ )
{
[output appendFormat:@"%02x", digest[i]];
}
return output;
}
else
{
return @"";
}
}
补丁状态的管理
可以毫不夸张的说,正确理解并定义补丁状态,是整个在线更新机制最核心的一步,其他的真的只是辅助:
/**
* 补丁状态.
*/
typedef enum : NSUInteger {
YFPatchModelStatusUnKnownError, //!< 未知错误.
YFPatchModelStatusUnInstall, //!< 尚未开始安装.应用初始时,所有本地补丁状态均为此;补丁更新或新增的补丁;在下载完成后,状态也会设置为此.
YFPatchModelStatusSuccess, //!< 安装成功.
YFPatchModelStatusFileNotExit, //!< 本地补丁文件不存在.
YFPatchModelStatusFileNotMatch, //!< 本地补丁MD5与给定的MD5值不匹配.
YFPatchModelStatusUpdate, //!< 此补丁有更新.即服务器最新返回的补丁列表中包含此补丁,但补丁的md5或url已改变.
YFPatchModelStatusAdd //!< 此补丁为新增的.即服务器最新返回的补丁列表中新添加的补丁.
} YFPatchModelStatus;
补丁状态的具体管理策略,参见 https://github.com/ios122/ios122/blob/master/iOS122/iOS122/samples/JSPatchOnline/patch/YFPatchViewModel.m
如何在本地测试JS可用性
这个是必然要考虑的问题,一种方式是可以在工程中放一个demo.js供Debug模式下调试;另一种方式是本地返回固定的假数据,但是假数据本身的 JS文件地址,md5,版本号等都是真实的.
/**
* 测试模式下,会执行此方法,以验证某个JS文件的作用.默认使用本地demo.js.
*/
- (void)mcDebug
{
#ifdef DEBUG
NSString * path = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
[self mcEvaluateScriptFile: path];
#endif
}
补丁的增删改查.
- 增:服务器返回的补丁,本地不存在时,会默认下载存储,并执行.
- 删: 服务器返回的补丁集中,不包含本地的某个补丁,则此补丁下次不会再被执行.
- 改: 服务器返回的补丁,本地包含,但md5值变化,此时会重新下载此补丁.
- 查: 会默认在应用启动时,执行所有存在,且md5值匹配的补丁.补丁集的信息,会在每次联网更新时更新.此处使用的是一个缓存库https://github.com/pinterest/PINCache
另外,整个逻辑的实现,还使用了ReactCocoa来简化逻辑代码,如果不是很熟悉,可以先看下:ReactiveCocoa,最受欢迎的iOS函数响应式编程库(2.5版),没有之一!
关于安全性
这个要根据自己App的情况,实际考虑下.我们的App网络接口是基于HTTPS的,所以不存在中间人攻击的情况.所以可以保证md5和文件路径是我们自己可控的,所以只做了最基本的md5校验.具体大家可以参考下官方的基于JSPatch的在线更新补丁实践http://jspatch.com/Docs/security:
JSPatch脚本的执行权限很高,若在传输过程中被中间人篡改,会带来很大的安全问题,为了防止这种情况出现,我们在传输过程中对JS文件进行了RSA签名加密,流程如下:
服务端:
计算 JS 文件 MD5 值。
用 RSA 私钥对 MD5 值进行加密,与JS文件一起下发给客户端。
客户端:
拿到加密数据,用 RSA 公钥解密出 MD5 值。
本地计算返回的 JS 文件 MD5 值。
对比上述的两个 MD5 值,若相等则校验通过,取 JS 文件保存到本地。
官方有个内测的平台http://jspatch.com,来支持在线更新,但是我做的时候,是不知道的,有点重复造轮子的感觉.但是,也就两天左右就实现了,只要能捋顺补丁状态控制的时机,代码本身其实并没有真正的技术难点.另外,官方的内测平台,好像是闭源的,我不太敢用.
关于JS文件的编写.
可以先参考下文档https://github.com/bang590/JSPatch/wiki/defineClass使用文档,其实都是一一对应的语法转义.如果代码很多,官方还提供了转换工具:https://github.com/bang590/JSPatchConvertor,但是结果仅供参考,可能还需要二次修改.
关于 APPstore 审核
我们的App,嵌入了JSPatch来进行Bug修复,已经通过审核,并且刚好修复了一个很紧急的Bug.这里不做过多的口水式的讨论.
小结
如果还在为每次的APP更新而提心吊胆,请细细阅读这篇文章;在线更新的,不仅仅可以用来修复Bug呦~
价值100W的经验分享: 基于JSPatch的iOS应用线上Bug的即时修复方案,附源码.的更多相关文章
- 基于Python接口自动化测试框架+数据与代码分离(进阶篇)附源码
引言 在上一篇<基于Python接口自动化测试框架(初级篇)附源码>讲过了接口自动化测试框架的搭建,最核心的模块功能就是测试数据库初始化,再来看看之前的框架结构: 可以看出testcase ...
- 【分享】自己写的一个可空的DateTimePicker控件-附源码
最近这段时间在重构以前的一个项目,其中有一项就是要把DateTimePicker控件值可空.大家都知道的DateTimePicker值为DateTime类型,DateTime类型值不能等于Null.但 ...
- 基于网格的波动方程模拟(Wave equation on mesh)附源码
波动方程是偏微分方程 (PDE) 里的经典方程,它在物理学中有大量应用并经常用来解释空间中的能量传播.波动方程是一个依赖时间的方程,它解释了系统状态是如何随着时间的推移而发生变化.在下面模拟波动方程时 ...
- 基于vue与vux做的可滑动tab组件(附源码)
背景 前不久,刚完成了一个商品列表+购物车功能的页面,因为一级商品分类在顶部tab中显示,可滑动,间距可定制,如下图所示: 定制的tab需求如下: 1. 每个tab-item的间距是相同的,可定制 2 ...
- 分享一组很赞的 jQuery 特效【附源码下载】
作为最优秀的 JavaScript 库之一,jQuery 不仅使用简单灵活,同时还有许多成熟的插件可供选择,它可以帮助你在项目中加入漂亮的效果.这篇文章挑选了8个优秀的 jQuery 实例教程,这些 ...
- 基于Redis缓存的Session共享(附源码)
基于Redis缓存的Session共享(附源码) 在上一篇文章中我们研究了Redis的安装及一些基本的缓存操作,今天我们就利用Redis缓存实现一个Session共享,基于.NET平台的Seesion ...
- 基于jQuery左右滑动切换特效 附源码
分享一款基于脚jQuery左右滑动切换特效.这是一款鼠标点击左右箭头按钮图片滚动切换,鼠标移到图片上显示透明边框特效. 效果图如下: 废话不多说,代码奉上! html代码: <div ...
- 干货——基于Nop的精简版开发框架(附源码)
.NET的开发人员应该都知道这个大名鼎鼎的高质量b2c开源项目-nopCommerce,基于EntityFramework和MVC开发,拥有透明且结构良好的解决方案,同时结合了开源和商业软件的最佳特性 ...
- 一文详解如何用 TensorFlow 实现基于 LSTM 的文本分类(附源码)
雷锋网按:本文作者陆池,原文载于作者个人博客,雷锋网已获授权. 引言 学习一段时间的tensor flow之后,想找个项目试试手,然后想起了之前在看Theano教程中的一个文本分类的实例,这个星期就用 ...
随机推荐
- java.lang.IllegalArgumentException: You must not call setTag() on a view Glide is targeting
将原有项目图片加载框架picasso改为glide,关于picasso和glide文档就自行查阅相关资料 显示 图片 例子 Glide.with(mContext).load(imageUrl).pl ...
- git常用命令-基本操作
git常用命令-基本操作 1) 新增文件 新增了Test_1.java git add Test_1.java git commit –m “新增了Test_1.java” git push ...
- Linux 的启动流程(转)
原文链接:http://blog.jobbole.com/46078/ 半年前,我写了<计算机是如何启动的?>,探讨BIOS和主引导记录的作用. 那篇文章不涉及操作系统,只与主板的板载程序 ...
- C# inline-hook / api-hook
我查阅了一下相关C#方面的资料,却没有发现有提供过关于api-hook方面的资 料包括应用库由此本人编写一套inline-hook的库用于支持x64.x86上的基于在 clr的公共语言,如: c#.c ...
- android 打包流程
.使用Android SDK提供的aapt.exe生成R.java类文件 .使用Android SDK提供的aidl.exe把.aidl转成.java文件(如果没有aidl,则跳过这一步) .使用JD ...
- saiku 分布式实践
saiku比较吃内存,一旦人多了,那么内存可能不够,所以会考虑主从结构,分担压力.为了保证数据的稳定性,也会有类似的考虑,那么问题来了,如何实现saiku的分布式搭建哪? 我阅读了一些国内的文章,没有 ...
- 对Joint Training of Cascaded CNN for Face Detection一文的几点疑惑
最近读了Joint Training of Cascaded CNN for Face Detection这篇论文,论文中把之前人脸检测使用到的cascade cnn,从分开训练的模式,改为了联合训练 ...
- Git 执行 「fork 出来的仓库」和「最新版本的原仓库」内容同步更新
当我们在 GitHub 上 fork 出一个仓库后,如果原仓库更新了,此时怎样才能保证我们 fork 出来的仓库和原仓库内容一致呢?我们一般关注的是仓库的 master(主干分支)的内容,通过以下步骤 ...
- 非常简单实用的Python HTTP服务
在做分布式系统应用的时候经常在测试环境上传一个包,或者干嘛的,公司的服务器比较bug,只给ldap权限,每次只能scp到自己的个人目录下,然后才能进到公共账号下去cp,比较麻烦.这时候如果你需要一个简 ...
- 用SQL server导出到oracle,查询时提示“表或视图不存在ORA-00942”错误
用SQL server2005的导出工具,将数据导出表到oracle,表名称里看到有这张表了,但查询或删除时都提示“ORA-00942表或者试图不存在”的错误,上网查了一下,是如下原因: “查询或删除 ...