熟悉 iOS\macOS Hybrid 混合开发的同学应该都有体会,WKWebView 虽然是苹果作为替代 UIWebView\WebView 而推出的"新"组件,但大部分开发者对它实在“爱不起来”。毕竟对于国内大部分应用开发者来说,在实际使用中 WKWebView 所谓的“优势”未必能体现出来,但带来的“坑”却都着实都不浅。
目前社区或线上可查找的 WKWebView 相关资料,大多比较陈旧且人云亦云、复制粘贴类的居多。少部分真实实践和探索的开发者,或许也因时间或精力的原因,对问题和解决方案未能做详细的阐述。导致目前线上 WKWebView 相关的资料数量不少、但质量不高;且有不少文章存在对问题的背景解释不清,解决方案缺乏有效验证等问题。
我从事端容器领域开发多年,曾在生产环境方案设计上与 WKWebView "对抗"多次。目前混合开发已经是现代 App 标配,一方面是对这么长时间用法经验上的总结,另外一方面也希望能够为还在抗争中的同学提供一些新视角或者解决思路,故准备结合 WebKit 部分源码,将自己对这个组件的理解以及部分问题解决方案整理分享一下。本文尝试说明 3 件事情:
- WKWebView 使用中的典型问题有哪些
- 为什么会出现这些问题
- 这些问题的解决办法有哪些
iOS 端网络设计和 WKWebView 设计特点我们可以通过官方资料来查阅。但为了后面更好的说明问题,下面我们重点回顾下与文章后续内容相关的两个基本知识点:
- iOS 端网络设计与 Cookie 管理
- WKWebView 多进程模型
1 iOS 网络设计与 Cookie 管理
Cookie 管理是做混合开发过程中经常会涉及到的部分,在应用开发中我们知道可以通过 NSHTTPCookie 和NSHTTPCookieStorage 来管理应用的 Cookie。但在系统层面 Cookie 是如何管理的、如何与网络层各模块进行联动,这对我们后面分析WKWebView 中的 cookie 问题有着至关重要的联系。
根据官方资料,我们可知 iOS 平台下网络相关模块大概关系如下:
从上至下模块依次为:
- WebKit:应用层,客户端 App 以及 WKWebView 处于这一层。
- NSURL:可以理解为对底层 CF 接口的封装扩展层,NSURLConnection、NSURLSession 等处于这一层。
- CFNetwork:iOS 网络模块核心实现层,是网络层设计中最重要的部分。负责网络协议组装发送接收等主要工作,与 CoreFoundation 框架关系紧密。
- BSD socket:基于底层硬件接口的 socket 服务。
CFNetwork 是整个网络系统的核心模块,负责组装请求、处理响应等:
核心内容包含:
- CFURLRequest:包括 URL/header/body 这些请求的信息。CFURLRequest 会进一步转换成 CFHTTPMessage。
- CFHTTPMessage:主要是 HTTP 协议的定义和转换,把每一个请求 request 转换成标准的 HTTP 格式的文本。
- CFURLConnection:主要是处理请求任务。包括 pthread 线程、CFRunloop、请求队列的管理等等。提供了start、cancel 等等操作的 API。
- CFHost:负责 DNS,有 CFHostStartInfoResolution 等函数,基于 dns_async_start 和 getaddrinfo_async_start 等方法。
- CFURLCache/CFURLCredential/CFHTTPCookie:处理 缓存/证书/cookie 相关的逻辑,都有对应的NS类。
从上面分析可知关键信息:iOS Cookie 管理相关模块处于 CFNetwork 这一层中。即对于请求 Response 中的 "set-cookie" 字段,在 CFNetwork 中被消费和处理。
2 WKWebView 多进程模型
通过官方资料,我们知道 WKWebView 相比 UIWebView 很大的一个变化是"多进程模型":
WKWebView 在运行时,核心模块运行在独立进程中,与 App 进程独立。
WKWebView 使各种问题的原因,有不少和多进程运行模式有很大的关系。
多进程模型详解
但具体是什么样形态的多进程?我们通过一张简图来说明下: