引言:在多元化的今天,一个热门的移动app,或多或少都会有内在H5在其中。而对于一个有不少运营场景的app来讲,这种状况更常见了。试想一下,若是在一个公司,存在不少native和H5同时须要开发的页面,为了节省开发成本,此时若是只开发H5,就须要考虑native的体验了,而这就是本文的目的,如何让native端拥有像加载本地页面同样的速度去加载H5。css
在app内加载H5速度慢一直是客户端开发的痛点,抛开H5的体验自己与native就有差距不说,若是加载速度还很慢,这将会对用户体验形成巨大影响。那么像作到像native页面同样瞬间加载完H5,思路就会变得比较清晰了--提早在本地存储远程资源包。html
从这个点出发,咱们须要考虑,以怎样的形式来提早拿到资源包(css,js,html,通用的图片等),减小这些静态资源的网络请求,增长加载速度。无非就是一下两点:ios
1.将资源包在app打包阶段直接植入web
2.在运行时动态下载资源包网络
单纯从业务层来讲,若是你的业务够简单,其实第一种方式已经彻底知足,每次须要新增页面就从新发版嘛,虽然显得有点愚笨,可是仍是能知足的。app
可是从长远的角度来讲,咱们要作到尽量的动态化,动态化是客户端的热点,咱们要作到尽可能不依赖于版本更新来实现动态化。对于iOS来讲,更新机制自己就很是缓慢,要经过app store的审核有时候还须要靠人品,更况且用户也不必定买帐,他们不必定会更新咱们的app。在这样的状况下,第二种方案就会显得更加友好。socket
那么,该怎么设计一套完整的解决方案来知足运行时动态下载资源包呢。ui
抽出细节,大致上能够归结为下图所示的结构图:url
我来解释下这个图,我是创建在客户端已经实现socket层协议,因此能与server保持长链接以致于server能主动push数据的状况,实现这种协议蛮复杂的。实际上若是没有这个协议,那就须要client找时机主动去server请求(app启动时请求一次?或者是每隔一段时间请求一次,取决于你),本文之后者为例。设计
下面我来演示下一个完整的下载新资源包的过程:
1.运营小妹以为某节日要到了,须要发布一个新的页面,而后在运营后台生成资源包,运营后台会自动更新config,其中包括资源包的version,是否强制关闭加载本地资源包(降级策略,防止这个组件自己有BUG),还有一些hotpatch脚本。而且将资源包根据里面的内容部署到remote database。
2.在合适的时机,client发起http请求向server查询是否有新版本的资源包,并带上本地的config。
3.server根据config里的选项,比对从client拿到的config,发现客户端是旧版本的config,OK,则下发新的config给client,而且发送从database里拿到的资源包(为了加快速度,能够部署到CDN)。
4.client拿到最新的资源包后,在本地进行解密,解压等操做,并映射成对应ULR相对于本地的local file url。好比:www.baidu.com这个网址下的静态资源文件在本地的的file://dsalkfjsld…
至此,已经完成资源包的下载。
那么有了资源包后,怎么能让app像native页面的速度去加载H5呢。
其实原理就是对H5请求进行拦截,若是本地已经有对应的静态资源文件,则直接加载,这样就能达到“秒开”webview的效果。
对于iOS而言,这就须要用到NSURLProtocol这个神器了。接下来,分析下它究竟是什么东西,咱们怎么利用它达到上述效果。
NSURLProtocol可以让你去从新定义苹果的URL加载系统(URL Loading System)的行为,URL Loading System里有许多类用于处理URL请求,好比NSURL,NSURLRequest,NSURLConnection和NSURLSession等。当URL Loading System使用NSURLRequest去获取资源的时候,它会建立一个NSURLProtocol子类的实例,你不该该直接实例化一个NSURLProtocol,NSURLProtocol看起来像是一个协议,但其实这是一个类,并且必须使用该类的子类,而且须要被注册。
--从网上拷贝的
换句话说,NSURLProtocol能拦截全部当前app下的网络请求,而且能自定义地进行处理。
废物很少说,上代码:
这里只介绍与咱们需求相关的NSURLProtocol方法。
搞了这么多,其实最核心的就是前四个方法:
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
这个方法的做用是判断当前protocol是否要对这个request进行处理(全部的网络请求都会走到这里,因此咱们只须要对咱们产生的request进行处理便可)。
+ (NSURLRequest )canonicalRequestForRequest:(NSURLRequest )request
这个方法其实很强大,它能够对request进行预处理,好比对header加一些东西什么的,咱们这里没什么要改的,因此直接返回request就行了。
- (void)startLoading
重点是这个方法,咱们这里须要作一件事,就是本身拼装httpResponse,而且返回给url load system,而后到了webview那一层,会收到response,对于webview而言,加载本地和走网络拿到的response是彻底同样的。因此上述代码展现了如何拼装一个httpResponse,当组装完成后,须要调用self.client将数据传出去。
何为self.client,这个东西其实就是protocol与url load system交互的一个对象,系统提供给咱们的,这样理解就够了。
须要注意的是,细心的读者会看到else里会有一段代码:
[NSURLProtocol setProperty:@YES forKey:WDHybridResourceProtocolHandledKey inRequest:newRequest];
这个是干什么用的?else的做用是当本地不存在这个文件时,则主动从新发请求,此时又会调用canInitWithRequest,若是不设置flag,则会无限递归了。因此你懂得。
固然,从新发请求天然要实现NSURLConnectionDelegate。
至此,如何快速加载H5已经所有介绍完毕。
附上先后加载速度对比:
好的,下次再见!
欢迎你们关注微博@kuailejim,我会常常在上面分享一些你们感兴趣的东西。