系列文章:
- 《37- WKWebView项目实践分享(一)- UIWebView回顾介绍》
- 《38- WKWebView项目实践分享(三)- native和webView交互》
- 《39- WKWebView项目实践分享(四) - 先了解下Cookie》
- 《40- WKWebView项目实践分享(五)- WKWebView如何加Cookie》
- 《41- WKWebView项目实践分享(六)- 项目实践:User Agent、跨域、重定向及其它》
- 《42- WKWebView项目实践分享(七) - 补充: 实践中的坑》
前言
本文中,我们先介绍一下WKWebView,对比UIWebView它的优缺点。然后再看下WKWebView的主要API和其中的属性,基本使用。
WkWebView的优点和注意点
在2014年的WWDC上,苹果针对WKWebView有专门的讲解。下边,通过WWDC的课件上来看一下WKWebView的特点:
WKWebView的多进程结构
上图中提到,WKWebView的Multi-process Architecture
多进程结构具有【更完整的执行流程控制】和【低能耗】的两个特点。下边,针对这两个特点详细解释一下:
一般而言,一个程序开启之后就会占用一个进程,但是这不是唯一的。也存在一个程序对应多个进程(比如说程序的"多开"),或者一个进程对应多个程序的情况(比如说一个系统级别的工具DirectX,你同时打开多个游戏都会使用到它)。 对于WKWebView,你每创建一个webView出来,默认的都会对应一个叫做Web Content Process
的进程。当然,你也可以不多对对
,可以多对一
。这样就可以使创建出的多个webView对应同一个的Web Content Process
的进程。这个特点也涉及到了一个共享缓存数据的问题,后边文章会详细提到。
基于这个特点,我们打开一个应用程序A,然后又在应用A中打开一个webView,这个时候其实是两个进程,没错, WKWebView在APP进程之外单独开了一个进程, 这一点非常重要。如下图:
因为WKWebView开辟的是独立于App之外的进程,webView占用的内存是转移到了自己的进程中,而不是在App的进程中。所以我们在查看内存占用情况的时候会看到,加载同一个网页的时候(复杂绘制的大型网页更明显),使用WKWebView的App会比使用UIWebView的App占用内存小。 也许有同学会想到,这有什么用呢,WKWebView不仍然还是在占用内存。我们举一个例子来说明这么设计的好处。当开启一个webView十分耗费内存并且超过了苹果的安全值时,使用UIWebView的App会被系统直接杀掉。而使用WKWebView的App,被杀掉的只是WKWebView,体现在APP上的就是webView白屏了。此时,我们依然可以退出webView所在的界面,继续愉快的玩耍App的其他功能。
另外,关于占用内存低
这个特点。我们监测内存的时候会发现,除了刚开始打开webView的时候,WKWebView会比UIWebView占用内存低之外。在后续的用户操作中,WKWebView仍然会很好的内存控制,而用了UIWebView的APP占用的内存会呈现出上涨。我觉得,这也还是因为WKWebView多进程架构的功劳。如下图:
WkWebView的注意点
在实际业务的接入和线上使用过程中,WKWebView有些地方并不方便,比如Cookie
和黑魔法NSURLProtocol
。
Cookie: 在WKWebView中,并不能像UIWebView那样很方便的设置Cookie,虽然可以通过其它方式实现,但是总体给人的感觉是Cookie并不在最开始苹果设计WKWebView的规划之内。之所以这样,猜想是苹果团队在设计WKWebView的时候,就没想过要让客户端干预Cookie,而是Cookie由HTML5后台完全控制,这么做是为了保证敏感数据安全。所以在iOS11以下的版本API中,并没有类似UIWebView中NSHTTPCookieStorage这样的可以直接设置和存储Cookie的官方类。但是在各个App的实际开发过程中,给HTML5后台传入Cookie进行身份校验等属于最最基本的操作,不可能按照苹果的设想去进行。也正是因为不能方便设置Cookie的原因,在本文发布的时候,很多大厂App的核心web业务并没有使用WKWebView,仍然是UIWebView。iOS11开始,苹果意识到了这个问题,做了妥协,新增WKHTTPCookieStorage来帮助开发者解决Cookie问题,虽然方便了,但是API仍然不稳定,比如iOS11.3的时候就出现了一些变动。具体Cookie到底是什么,然后项目中解决Cookie的步骤,我单独写了两篇文章来详细说明。《39- WKWebView项目实践分享(四) - 先了解下Cookie》和《WKWebView项目实践分享(五)- WKWebView如何加Cookie》
NSURLProtocol: 黑魔法在WKWebView是不被苹果所允许的,网上有调用私有API来获取和实现
NSURLProtocol
的方法,虽然可以实现其部分功能,但是仍然有很多问题和风险。我在后续接入WKWebView的过程中,直接舍弃了NSURLProtocol
。下图是
Cookie
和NSURLProtocol
在iOS11.3及其以前版本中存在的问题:
其它需要注意的地方:
- 有的webView页面出现偏移
- 需要自己处理AlertView弹框
- 需要自己处理a标签和target=_blank
- 页面的重定向
- 跳转到支付宝、打电话、打开Appstore等需要自己处理openURL:
- OC调用JS的方法是异步的
- 修改User-Agent
- 跨域请求
- 白屏问题
当然了,还有其它需要注意的地方,上边只是在我的应用中遇到的和解决的。关于其它大家可以自行网上搜索或者看我最下边提供的参考文章的链接。
在下一篇文章里,我结合自己项目来说说基于当前iOS11.3下是如何解决这些问题的。
WebKit介绍
UIWebView属于UIKit框架,而WKWebView属于独立的WebKit框架。这个框架中的所有API都和webView有关。从这里也可以看出,WKWebVie拥有的API是非常丰富的,你能更加细致的把控webView的展示和交互过程。
另外,WKWebView的结构和UIWebView的结构不同,如下:
WkWebView常用API概览
下图中,是WkWebView所涉及到的相关类及其作用:
下图中,对上图中的类按照功能作用进行柜类,可以分为如下几种:
WkWebView简单使用和说明
1. WKWebViewConfiguration和初始化
相较于UIWebView,WKWebView在初始化的时候可以传入一个WKWebViewConfiguration
对象。这个WKWebViewConfiguration
的作用是对WKWebView进行相关设置,比如,你可以设置否进行在线自动播放、复制内容的精确度、给webView添加JS代码等
。
以下几行代码,你就可以正常显示一个webView,如下:
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
// ...这里可以进行设置 configuration.xxx = xxxx;
WKWebView *webView = [[WKWebView alloc] initWithFrame:myFrame configuration:configuration];
NSURL *URL = [NSURL URLWithString:@"https://www.baidu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
[webView loadRequest:request];
如果要进行额外的设置,在WKWebViewConfiguration
中可能用到的一些类以及其作用,如下:
2. WKWebView的三大代理
使用代码如下:
wkWebView.navigationDelegate = self; // WKNavigationDelegate
wkWwebView.UIDelegate = self; // WKUIDelegate
//...WKScriptMessageHandler的使用,在后续中文章专门讲解
- WKNavigationDelegate
先说一下WKNavigationDelegate
,它提供了WKWebView在整个加载过程中不同阶段【比如开始加载、正在加载、加载成功、加载失败等】的回调方法。回调方法很多,可以让我们掌控webView更多的加载细节,然后对其加载过程进行干预。
下面两图中,我们可以看出WKWebView和UIWebView相比,新增加了哪些阶段可以被开发者所操作:
下图是在webView请求过程中,WKNavigationDelegate提供的方法和UIWebView代理方法的区别:
另外,WKNavigationDelegate还有两个重要的代理方法如下:
// 当WKWebView对应进程退出时回调
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
// 当一个正在提交的页面在跳转过程中出现错误时调用这个方法
- (void)webView:(WKWebView *)didFailNavigation:(WKNavigation *) withError:(NSError *)
-
WKUIDelegate
WKScriptMessageHandler
3. 方法的参数解释
上边我们已经介绍了WKWebViewConfiguration
的相关属性,在这里再介绍一下WKUIDelegate和WKNavigationDelegate中的其它的一些参数,以帮助我们更好理解WKWebView的设计意图的使用它。
- WKFrameInfo
包含了一个网页页面的框架frame信息:
- mainFrame:是否是主窗口(框架)
- request:窗口(框架)的请求
- securityOrigin:框架的安全源
- webView:WkWebView
- 拓展:关于HTML中的frame
这里是从 W3CSchool得到的一些信息,如果像更多的了解frame,可以查一下W3CSchool:
- 标签:定义 frameset 中的一个特定的窗口(子窗口)(框架)
- 但是由于 标签对网页可用性的负面影响,在 HTML 5 中 标签没有得到支持
通过WKFrameInfo,可以得到【当前frame是不是当前HTML页面的主frame】等信息。
- WKNavigationAction
WKNavigationAction存在的方法之一是decidePolicyForNavigationAction:
,该方法是webView刚开始准备向服务器发起请求的时候被调用。其中的几个属性的作用如下:
- 通过request我们可以得到发起的请求的信息,包括HttpHeader、cookie、User Agent等。
- 通过navigationType我们可以来判断进入当前回调方法的操作是返回上一页,还是前进到下一页,还是重新刷新或者新加载一个URL等。
- 通过sourceFrame和targetFrame(destinationFrame)可以得到当次请求来自的HTML页面的frame的信息。
- WKNavigationResponse
WKNavigationResponse是在webView请求发送到服务器,然后服务器做出了响应,App接收到响应之后调用的方法decidePolicyForNavigationResponse:
中。 通过其中的NSURLResponse可以得到请求回调的内容。
Decision Handle(请求决策者)
下边这两个方法是发起请求前和收到请求后的回调:-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler -(void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
两个方法中参数block
(void (^)(WKNavigationResponsePolicy))decisionHandler
的作用是是否继续执行后续请求流程。
在UIWebView中,判断是否执行当前webView的请求是在shouldStartLoadWithRequest:
方法中,我们通过返回YES或者NO来决定。 在WKWebView中,在请求发起之前和接收到服务器返回的方法中都可以判断当前的webView是否是自己想要的,是否继续执行请求。
- WKNavigationActionPolicyCancel 停止继续请求
- WKNavigationActionPolicyAllow 继续请求
- WKNavigationResponsePolicyCancel
- WKNavigationResponsePolicyAllow
- WKUserScript 我们在后续单独的文章专门讲解。
参考
- 《Apple Documentation》
- 《教你使用 WKWebView 的正确姿势》
- 《NSURLProtocol 全攻略》
- 《WKWebView详解》
交流
希望能和大家交流技术
Blog:http://www.lilongcnc.cc
·