WebView 调试

页面重载机制

Posted by poos on January 30, 2024

简介

web 调试可能碰到种种问题。那么如何处理调试呢?

问题一、webViewWebContentProcessDidTerminate

选择了一个图片后,webview 重新加载了?

背景

在这篇文章有详细的系统函数调用上下文、流程等。 深入理解WKWebView白屏

简单来说,系统会因为如下的不同的原因,进入不同的流程。具体是在内存超出、CPU超出、以及发生了Crash的场景下需要重新刷新(1次,内部是有一个计数)。其他情况下保持原状。

1
2
3
4
5
6
7
ProcessTerminationReason {
    ExceededMemoryLimit,//超出内存限制
    ExceededCPULimit,//超出CPU限制
    RequestedByClient,//主动触发的terminate
    Crash,//web进程自己发生了crash
    NavigationSwap,//m页的加载环境出现了变化
}

而开发者可以介入,就是在 webViewWebContentProcessDidTerminate 方法。但是遗憾的是这个方法没有传入 reason。

1
2
3
protocal WKNavigationDelegate {
    func webViewWebContentProcessDidTerminate(_ webView: WKWebView);
}
简单深入

其实苹果是有一个私有方法,可以拿到reason。使用如下的示例代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CWebview: WKWebView {
}

extension CWebview: WKNavigationDelegate {
    func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
        NSLog("-----17web ter: \(webView)")
        webView.reload()
    }
    @objc
    func _webView(_ webView: WKWebView, webContentProcessDidTerminateWithReason reason: NSInteger) {
        NSLog("-----22web ter \(reason): \(webView)")
        webView.reload()
    }
}

代码运行后发现:

  • 优先 webContentProcessDidTerminateWithReason,这两个方法只会调用一个(也是符合一贯的风格)。
  • 要声明 @objc 才能被调用到。

如何调试

有个文档可以非常有效的在模拟器进行测试:

使用 kill $(pgrep -P $(pgrep launchd_sim) 'com.apple.WebKit.WebContent') 即可触发移除web crash类型的异常。

详细的查看如下文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# In the command line, find the PID of your simulator process:
ps -p `pgrep launchd_sim`

# or if you have many simulators running:
ps -A | grep launchd_sim

# Find the PID of the WebContent process:
pgrep -P <simulator-pid> 'com.apple.WebKit.WebContent'

# kill it
kill <webcontent-pid>

# or if you want a one-lner:
kill $(pgrep -P $(pgrep launchd_sim) 'com.apple.WebKit.WebContent')

模拟器,从命令行终止 Web 内容进程以触发调用webViewWebContentProcessDidTerminate

真实设备上的话,暂没找到可以触发方式。可预见的是OOM类型的可以通过其他app配合调试。

一点思考,为啥 WKNavigationDelegate 不用显式声明 @objc 呢?

尝试一下几个解法: 1、如上 @objc ,可以 2、如下 @objc ,可以

1
2
3
4
5
6
7
8
9
10
11
12
@objc
extension CWebview: WKNavigationDelegate, CWKNavigationDelegate {
    func _webView(_ webView: WKWebView, webContentProcessDidTerminateWithReason reason: NSInteger) {
        NSLog("-----22web ter \(reason): \(webView)")
        webView.reload()
    }
}

@objc
protocol CWKNavigationDelegate {
    func _webView(_ webView: WKWebView, webContentProcessDidTerminateWithReason reason: NSInteger);
}

3、如下 @objc ,不可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@objc
extension CWebview: WKNavigationDelegate, C2WKNavigationDelegate {
    func _webView(_ webView: WKWebView, webContentProcessDidTerminateWithReason reason: NSInteger) {
        NSLog("-----22web ter \(reason): \(webView)")
        webView.reload()
    }
}

@objc
protocol CWKNavigationDelegate {
}

protocol C2WKNavigationDelegate {
    func _webView(_ webView: WKWebView, webContentProcessDidTerminateWithReason reason: NSInteger);
}

4、参考 WKNavigationDelegate,继承自 NSObjectProtocol,不可以

看来纯纯就是要声明一下 @objc,其他花里胡哨的都没有用。@objc也不支持传递。 protocal 也不能用 final, 所以,这个场景咱还是直接加 @objc 在函数上保险。 而系统的可以不用声明,大概率是编译器做了适配。