内所有的网络请求进行拦截

作者: 时时彩平台-编程  发布:2019-09-13

WKWebView不支持NSURLProtocol

前段时间总结过《WKWebView从入门到趟坑》,其中提到 NSURLProtocol 拦截支持和缓存的痛点。在 UIWebView 时代,按照下面的方式注册一个自定义的 NSURLProtocol 和 CustomURLCache 的子类,

// 注册url拦截[NSURLProtocol registerClass:[CustomURLProtocol class]];// 注册缓存加载[NSURLCache setSharedURLCache:[CustomURLCache new]];

然后就可以在其实现中对 app 内所有的网络请求进行拦截,并加载本地的离线资源(很多 Hybird 框架都是基于此原理)。但在 WKWebView 中的请求却完全不遵从这一规则,网络上文章一般都解释说 WKWebView 的请求是在单独的进程里,所以不走 NSURLProtocol。

图片 1IPC.jpg

断点调用栈也显示 WKWebView 在加载 page 时候的确使用的是 WebKit 的 IPC 消息收发/分发机制 MessageQueue 进行进程之间的通信,但也发现它竟然调用到了

+ [NSURLProtocol canInitWithRequest:]

之后的流程就不在执行 NSURLProtocol 相关的方法了,既然调用了一个 canInitWithRequest,至少说明 WKWebView 还是引用了 NSURLProtocol 类或其子类,只是不知道什么原因没有实现其他调用(很多人都评论 WKWebView 是未开发完善的半成品)。好在 WKWebView 的内核是基于开源的 WebKit,让好奇地开发者们能够翻看源码一探究竟,传送门。在一个测试用例下 AlwaysRevalidatedURLSchemes.mm,看到了熟悉的 WKWebView 加载流程代码,如下图:

图片 2testcase.jpg

注意红色标记处,正是 UIWebView 时代 NSURLProtocol 拦截方式,可见源码上 WKWebView 似乎是支持 NSURLProtocol 的,在 WebKit.framework 中并没有 WKBrowsingContextController 的身影,说明这是个未开放的类,它到底是个什么鬼呢?猜测它可能与 NSURLProtocol 有关。

WebKit 源码浅析

众所周知,WebKit 源码由三大部分组成:

WebCore:HTML 排版引擎核心,主要包含 Loader,Parser(DOM,Render),Layout,Paint等模块WebKit:移植层,主要包括 GUI,File System,Thread,图片解码等与平台相关的模块JavaScriptCore:JS 虚拟机,主要用于操作 DOM,解析执行 JavaScript 代码

其源码架构如下:

图片 3WebKit.jpg

与 WebKit.framework 相关的源码脑图如下:

图片 4webkit-framework.jpg

红色标记处即苹果并未开放出来的接口类,其中:

1、WKScrollView 负责 Page 的一些滑动,手势操作等。2、WebContenView 负责 Page 的渲染。3、WKBrowsingContextController 负责浏览器的交互处理,提供了一些通用的操作,如加载,前进,后退,刷新等。4、WKBrowsingContextControllerHandel 提供可扩展的操作的抽象,插件化实现各种浏览器功能。5、WebPageProxy 是 WebKit 的 Page 代理,实现 UI Process 层和 Web Process 层的接口。6、WebProcessPool 通过让所有 WKWebView 共享同一个 WKProcessPool 实例,可以实现多个 WKWebView 之间共享数据。不过 WKProcessPool 实例在 app 杀进程重启后会被重置,无法实现本地化保存。

参考博文中提到的:

图片 5flowchart.jpg

可以很清楚地了解 WKView(WKWebView 是其在 iOS 平台的一个实例)在 WebKit 中的初始化流程:

1、根据配置项 WKWebViewConfiguration 创建新 WKWebView,同时会初始化 WKScrollView 和 WKContentView;2、WKContentView 从进程池 WKProcessPool 中分配 WebProcessProxy 和 WebPageProxy,同时根据当前 Page 初始化 WKBrowsingContextController,提供了大部分交互操作功能;3、WKWebView 在独立于 App Process 进程之外的 Network Process 进程中执行网络请求,请求数据不经过主进程,因此,在 WKWebView 上直接使用 NSURLProtocol 无法拦截请求。

其中有两个方法是前面测试用例中见到的:

图片 6regist.jpg

可见内部对全局的 WebProcessPool 进行了自定义 scheme 的注册和注销。继续查看 WebProcessPool 的源码:

图片 7NetworkProcess.jpg图片 8platform.jpg图片 9platform2.jpg

发现其实 WKWebView 也会将 Cookie 存储于 NSHTTPCookieStorage,同样Cache 也会存储于 NSURLCache 中,只是 SandboxExtension 会把数据 encode 成一个 Message 通过 IPC 发送给 App Process 进行异步存储的,这就会存在时机问题,并不是业界普遍认为 WKWebView 拥有自己的私有存储。

在 globalURLSchemesWithCustomProtocolHandlers() 中获取的即是已注册的自定义 scheme。从前面的源码中可见这些已注册的 scheme 被当作配置参数 (NetworkProcessCreationParameters) 传递给 Network Process 进程,而在其初始化中

图片 10network.jpg

CustomProtocolManager 通过 WKCustomProtocol 负责拦截处理自定义的 scheme,同时把 Network Process 中发起的网络请求发送到 App Process 进程,交由 CustomProtocolManagerProxy 代理重新发起实际的网络请求,最后把响应信息交还给 WKCustomProtocol 进行处理。

图片 11CustomProtocolManager.jpg

总结一下流程如下:

图片 12flow.jpg

1、WKBrowsingContextController 通过 registerSchemeForCustomProtocol 向 WebProcessPool 注册全局自定义 scheme2、WebProcessPool 使用已注册的 scheme 初始化 Network Process 进程配置,同时设置 CustomProtocolManager,负责把网络请求通过 IPC 发送到 App Process 进程、也接收从 App Process 进程返回的网络响应 response3、CustomProtocolManager 注册了 NSURLProtocol 的子类 WKCustomProtocol,负责拦截网络请求处理4、CustomProtocolManagerProxy 中的 WKCustomProtocolLoader 使用 NSURLConnection 发送实际的网络请求,并将响应 response 返回给 CustomProtocolManager

至此,完成了一个完整的网络请求代理拦截处理流程。可见 WKWebView 源码是支持 NSURLProtocol 拦截的

如何让 WKWebView 支持 NSURLProtocol

既然知道了源码逻辑,就可以仿照测试用例中的代码来实验上述结论,这个已经有大牛完成了 Demo 例子,这里就只贴出自己测试时候的关键代码。

图片 13demo.jpg

实验结果证实 WKWebView 是支持 NSURLProtocol 拦截的,只是 WebKit.framework 还不完善

​额外提一下,越来越多的人开始适配 WKWebView,也趟了不少坑,总结了不少经验,其中腾讯 Bugly 公众号上总结的《WKWebView 那些坑》非常深入。也解决了本人遇到 post 请求 body 数据丢失问题的疑惑,受益匪浅。

本文由时时彩平台发布于时时彩平台-编程,转载请注明出处:内所有的网络请求进行拦截

关键词: