环境:
XCode 9.3
iOS8-iOS11.3
年初,项目替换了WKWebView,过程中也遇到了很多问题。基于这些问题,结合网上大家的解决方案,进行取舍以获得一个最好的方案。当然这个最好,仅限于我来说,如果大家还有更高的方案,希望能够多多交流,毕竟坑不少,大家一起填才能把坑才能填平。
这篇文章主要是对WKWebView进行一个介绍,和对UIWebView进行一个回顾。
UIWebView
UIWebView和HTML5的发展历史
首先我们来看这张表格:
UIWebView和HTML5的发展历史从表格里边我们可以看出,2008年早在iOS2.0的时候,UIWebView就已经发布了,但是HTML5的规范正式定稿和火热的使用是在2012年之后,中间差了4年。这4年时间对于互联网来说变化很大了。可想基于早期HTML标准设计的UIWebView自然就不可能完全的支持HTML5的特性了。另外,UIWebView使用的时候,内存占用巨大,native和webView交互单一,新的WebView也是呼之欲出。所以2014年WWDC,伴随着iOS8系统正式推出WKWebView。
UIWebView的API
打开UIWebView.h,会发现苹果提供的可操作的API很少。这就意味你在加载一个webView的时候,你能处理和掌控的环节和细节就很少。简单归下类,如下:
创建
UIWebView *webView = [[UIWebView alloc] initWithFrame:myFrame];
NSURLRequest *request = [NSURLRequest requestWithURL:myURL];
[webView loadRequest:request];
UIWebViewDelegate
// 是否允许加载网页,也可获取js要打开的url
(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
// 开始加载网页
(void)webViewDidStartLoad:(UIWebView *)webView;
// 网页加载完成
(void)webViewDidFinishLoad:(UIWebView *)webView;
// 网页加载错误
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;
Cookie管理
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
使用NSURLProtocol
// 注册代理
[NSURLProtocol registerClass:[self class]];
// 取消注册代理
[NSURLProtocol unRegisterClass:[self class]];
// 整个URL Loading System的入口,进行请求的过滤,筛选出需要进行处理的请求,表示是否可以处理这个request
(BOOL)canInitWithRequest:(NSURLRequest *)request;
// 开始请求
(void)startLoading;,
// 取消执行请求
(void)stopLoading;
// 发起NSURLSession请求, 以及NSURLSession相关代理方法
其他方法
- (void)reload;
- (void)stopLoading;
-
- (void)goBack;
- (void)goForward;
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack;
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward;
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
@property (nonatomic) BOOL scalesPageToFit;
UIWebView的优缺点
UIWebView当然也是那么的一无是处,毕竟你reveal看一下,好多大厂还是使用的它。
那么,UIWebView的优点:
- 操作Cookie很容易
- 可以使用NSURLRequestCachePolicy缓存策略
- 可以使用苹果的黑魔法NSURLProtocol,可以轻松定义整个URL Loading System
自定义NSURLProtocol后,就有机会对所有的请求进行统一的处理,基于这一点它可以让你:
- 自定义请求和响应
- 提供自定义的全局缓存支持
- 重定向网络请求
- 提供HTTP Mocking (方便前期测试)
- 其他一些全局的网络请求修改需求
关于NSURLProtocol和URL Loading System这部分,可以看一下网易乐得团队的文章《NSURLProtocol》,开头对于写的很言简意赅。
UIWebView的缺点
WKWebView的优点就是它的缺点, 比如说:
- 会导致APP占用内存急剧上升
- native与webView交互方式单一
- 对H5新特性支持不是很好
- API太少
好了,接下来看下WKWebView。
WkWebView
WkWebView的特点
在2014年的WWDC上,苹果针对WKWebView有专门的讲解。下边,通过WWDC的课件上的几张幻灯片来看一下WKWebView的特点:
新的WebKit框架设计特性1 新的WebKit框架设计2 多进程的特点WK的特点从上边的图里可以很清楚的得到。我们说一下WK多进程的特点。
一般而言,一个程序开启之后就会占用一个进程,但是这不是唯一的。也存在一个程序对应多个进程(比如说程序的"多开"),或者一个进程对应多个程序的情况(比如说一个系统级别的工具DirectX,你开多个游戏都会使用到它)。 对于WKWebView,你每创建一个webView出来,默认的都会对应一个叫做Web Content Process的进程。当然,如果你进行设置的话,那么就可以使创建出的多个webView对应同一个的Web Content Process的进程。这个特点也涉及到了一个共享缓存数据的问题,后边文章会详细提到。
基于这个特点,我们打开一个应用程序A,然后又在应用A中打开一个webView,这个时候其实是两个进程,如下图:
来自:《教你使用 WKWebView 的正确姿势》
这也就解释了为什么我们在监测内存的时候会看到,使用WKWebView的APP会比使用UIWebView的APP占用内存小.其实WKWebView占用的内存只不过是转移到了自己的进程中,而不是APP的进程中。 这么设计的好处就是,当WKWebView因为某些原因,比如内存占用很大被杀死的时候,不会影响到我们的APP跟着crash掉。crash的仅仅是WKWebView所占用的进程,体现在APP上的就是webView白屏。此时,我们依然可以退出WebView所在控制器然后继续APP的其他操作。
另外,关于内存占用低这个特点。我们监测内存的时候会发现,除了刚打开webView的时候,WKWebView会比UIWebView占用内存第一点外,更重的要是在后续的webVIew交互中,体现在APP占用内存体积上,WKWebView会很好的控制内存,而用了UIWebView的APP占用的内存会呈现出上涨。我觉得,这其实还是因为WKWebView多进程的特点。如下图:
占用内存低所以,这里我在想,APP内存+WKWebView的进程内存的和
对比使用了UIWebView的APP的内存大小
是怎么个情况呢?
WkWebView的注意点
当然了,WKWebView在展示出这么多的优点之后还是有需要注意的地方。我总结了几个点,如下:
两个比较严重的点需要注意
其它需要注意的地方:
- 有的webView页面出现偏移
- 需要自己处理AlertView弹框
- 需要自己处理a标签和target=_blank
- 页面的重定向
- 跳转到支付宝、打电话、打开Appstore等需要自己处理openURL:
- OC调用JS的方法是异步的
- 修改User-Agent
- 跨域请求
- 白屏问题
当然了,还有其它需要注意的地方,上边只是在我的应用中遇到的和解决的。关于其它大家可以自行网上搜索或者看我最下边提供的参考文章的链接。
在下一篇文章里,我结合自己项目来说说基于当前iOS11.3下是如何解决这些问题的。
WebKit
UIWebView属于UIkit框架,我们查看头文件所在的位置,可以看到UIWebView只不过是UIkit框架中的一个文件。
看WKWebView的话,它是在WebKit框架中的,WebKit框架中的其它类都和设置webView有关系。从这里也可一看出来,设WKWebView对应的API是非常丰富的,你能更加细致的把控webView的展示和交互过程。
WkWebView常用API概览
![API1,from:《WKWebView详解》
[图片上传失败...(image-e0c3d3-1524661907350)]
WkWebView简单使用和说明
1.初始化
WKWebView和UIWebView创建不同的地方是初始化的时候可以传入一个WKWebViewConfiguration对象进取。这个WKWebViewConfiguration对象相当于是WKWebView的偏好设置了,你可以在这里对是否进行在线自动播放、复制内容的精确度、给webView添加JS代码等等进行设置。
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKWebView *webView = [[WKWebView alloc] initWithFrame:myFrame configuration:configuration];
NSURL *URL = [NSURL URLWithString:@"http://en.wikipedia.org/wiki"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
[webView loadRequest:request];
关于WKWebViewConfiguration中的一些类,如下:
- WKProcessPool: 进程池
- WKPreferences: webView设置
- WKUserContentController: 处理JS相关
- WKWebsiteDataStore: iOS9.0之后出现,获取和移除
- WKWebView缓存,iOS11后,WKHttpCookieStore。
(!注意: 没有设置缓存的方法) - 其它:设置网页控制音视频自动播放的方法 ……
2.WKNavigationDelegate
wkWebView.navigationDelegate = self;
wkWwebView.UIDelegate = self;
关于这两个代理,我们先说一下WKNavigationDelegate,它提供了webView在加载过程的回调方法。上边说过,WKWebView能让你掌控更多的webView的加载细节和进行设置。 下边我们通过两个图看一下WKWebView相较于UIWebView的加载过程的不同:
来自:2104-WWDC 来自:《教你使用 WKWebView 的正确姿势》下图是在webView请求过程中,WKNavigationDelegate提供的方法和UIWebView代理方法的区别:
加载过程中的回调方法另外,WKNavigationDelegate还有两个重要的代理方法如下:
1. 当WKWebView对应进程退出时回调
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
2. 当一个正在提交的页面在跳转过程中出现错误时调用这个方法
- (void)webView:(WKWebView *) didFailNavigation:(WKNavigation *) withError:(NSError *)
3.WKUIDelegate
[图片上传失败...(image-3475b9-1524322215768)]
此处需要替换。
4.WKUIDelegate和WKNavigationDelegate中的参数
这里说一下WKUIDelegate和WKNavigationDelegate中的一些参数
,帮助我们更好的理解WKWebView的设计意图。
[图片上传失败...(image-31b77f-1524322215768)]
通过WKFrameInfo,可以得到这个frame是不是当前html页面的主frame等信息。如果像更多的了解frame,可以查一下W3CSchool。
WKNavigationAction是在在webView刚开始准备向服务器发起请求的时候回调的方法
decidePolicyForNavigationAction:
中。
- 通过request我们可以得到发起的请求的信息,包括HttpHeader、cookie、User Agent等。
- 通过navigationType我们可以来判断进入当前回调方法的操作是返回上一页,还是前进到下一页,还是重新刷新或者新加载一个URL等。
- 通过sourceFrame和targetFrame(destinationFrame)可以得到当次请求来自的HTML页面的frame的信息。
WKNavigationResponse是在webView请求发送到服务器,然后服务器做出了响应,App接收到响应之后调用的方法decidePolicyForNavigationResponse:
中。
通过其中的NSURLResponse可以得到请求回调的内容。
5.block:(void (^)(WKNavigationResponsePolicy))decisionHandler
在UIWebView中,判断是否执行当前webView的请求是在shouldStartLoadWithRequest:
方法中,我们通过返回YES活着NO来决定。 在WKWebView中,在请求发起之前和接收到服务器返回的方法中,两个地方都可以决定是否要将当前的webView有效展示。然后通过decisionHandler(WKNavigationActionPolicyCancel)和decisionHandler(WKNavigationActionPolicyAllow)表示是否继续执行请求。
参考
交流
希望能和大家交流技术
Blog:http://www.lilongcnc.cc
网友评论