如何感知 WebKit 页面切换
背景
通常在 WKWebView 打开一个页面,收到页面数据时,代理方法可感知这个时机:
-webView:didCommitNavigation:
但若是改变页面 hash (也就是位置标识符"#") 打开另一页面时,这个代理方法不会调用,也没有合适的回调接口。
从表现上说,改变 hash 会产生网页历史栈,safari 也会产生历史记录,这种场景应该是有和 -webView:didCommitNavigation:
相对应的回调,官方没做好一致性,需通过 WebKit 源码进一步探索感知方式。
源码分析
断点查看触发-webView:didCommitNavigation:
调用栈如下:
-> UIProcess 进程
-[AnyInstance webView:didCommitNavigation:]
WebKit::NavigationState::NavigationClient::didCommitNavigation
(IPC) WebKit::WebPageProxy::didCommitLoadForFrame
-> WebContent 进程
(IPC) Messages::WebPageProxy::DidCommitLoadForFrame
WebKit::WebFrameLoaderClient::dispatchDidCommitLoad
WebCore::FrameLoader::dispatchDidCommitLoad
WebCore::FrameLoader::receivedFirstData
在第一次收到页面数据后,会进行网页历史栈等状态的处理,最后抛给公开代理。对于改变页面 hash 打开另一页面场景,是在同一个 Document,FrameLoader
作为专门处理页面加载的地方,应该是有处理目标页面是否是同一 Document 的代码分支,扫描一下就找到了一个可疑的函数:
void FrameLoader::loadItem(...) {
...
if (sameDocumentNavigation)
loadSameDocumentItem(item);
else
loadDifferentDocumentItem(item, fromItem, loadType, MayAttemptCacheOnlyLoadForFormSubmissionItem, shouldTreatAsContinuingLoad);
}
追溯函数调用链,发现-webView:didCommitNavigation:
调用栈正是来自else
分支,查看if
分支的处理,最终会通过一个 IPC 消息发送到 APP 进程,在 APP 进程代码实现如下:
void WebPageProxy::didSameDocumentNavigationForFrame(...) {
...
m_navigationClient->didSameDocumentNavigation(...);
...
}
void NavigationState::NavigationClient::didSameDocumentNavigation(...) {
...
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState->m_webView navigation:wrapper(navigation) didSameDocumentNavigation:toWKSameDocumentNavigationType(navigationType)];
}
这里的navigationDelegate
就关联了 WKWebView 的公开代理 navigationDelegate
,而这个代理方法在私有代理方法列表躺着:
typedef NS_ENUM(NSInteger, _WKSameDocumentNavigationType) {
_WKSameDocumentNavigationTypeAnchorNavigation,
_WKSameDocumentNavigationTypeSessionStatePush,
_WKSameDocumentNavigationTypeSessionStateReplace,
_WKSameDocumentNavigationTypeSessionStatePop,
} WK_API_AVAILABLE(macos(10.10), ios(8.0));
@protocol WKNavigationDelegatePrivate <WKNavigationDelegate>
...
- (void)_webView:(WKWebView *)webView navigation:(WKNavigation *)navigation didSameDocumentNavigation:(_WKSameDocumentNavigationType)navigationType;
...
@end
粗略分析下源码,当 navigationType
这个枚举是 _WKSameDocumentNavigationTypeAnchorNavigation
时就表示完成了这次改变 hash 的页面切换。实现这个私有代理从源码来看是无副作用的,MR 记录在这里:https://bugs.webkit.org/show_bug.cgi?id=134855 。
结论
所以只需要在 WKWebView 的 navigationDelegate
所属类下面实现 _webView:navigation:didSameDocumentNavigation:
方法就能捕获到改变 hash 的页面切换的操作了,和 -webView:didCommitNavigation:
配对可完整感知 WKWebView 的页面切换完成时机。
相关文章
- 金融服务领域的大数据:即时分析
- 影响大数据、机器学习和人工智能未来发展的8个因素
- 从0开始构建一个属于你自己的PHP框架
- 如何将Hadoop集成到工作流程中?这6个优秀实践必看
- SEO公司使用大数据优化其模型的5种方法
- 关于Web Workers你需要了解的七件事
- 深入理解HTTPS原理、过程与实践
- 增强分析:数据和分析的未来
- PHP协程实现过程详解
- AI专家:大数据知识图谱——实战经验总结
- 关于PHP的错误机制总结
- 利用数据分析量化协同过滤算法的两大常见难题
- 怎么做大数据工作流调度系统?大厂架构师一语点破!
- 2019大数据处理必备的十大工具,从Linux到架构师必修
- OpenCV中的KMeans算法介绍与应用
- 教大家如果搭建一套phpstorm+wamp+xdebug调试PHP的环境
- CentOS下三种PHP拓展安装方法
- Go语言HTTP Server源码分析
- Go语言HTTP Server源码分析
- 2017年4月编程语言排行榜:Hack首次进入前五十