如何防止WKWebView对象崩溃?

5

场景

我正在使用 Swift 构建一个 iOS 应用程序。其中一个功能是将实时视频流作为应用程序的背景。视频流源自局域网中的 Raspberry Pi,使用 sudo motion。Motion 成功在默认端口 8081 上托管了该视频流。

Swift 应用程序有一个 WKWebView 对象,其源指向我的 Raspberry Pi 上的 motion 端口。

疑似问题

端口 8081 上的网页不断刷新以加载来自相机的最新帧。

问题

运行应用程序时,视频流成功连接并加载第一帧,并偶尔加载第二帧,然后就中断了。

在某些情况下,我在终端中收到以下错误:[ProcessSuspension] 0x282022a80 - ProcessAssertion() Unable to acquire assertion for process with PID 0 这让我相信这是与网页不断刷新的性质相关的内存管理问题。

当前配置

目前,我的调用 .load() 的 WKWebView 对象在 ViewController.swift > override func viewDidLoad() 中。

建议解决方案

我是否需要构建某种循环结构,在加载一帧、暂停执行后,几秒钟后再调用 WKWebView 来重新加载新的一帧。

我非常新手 Swift,对我的问题格式的耐心高度赞赏。


提交的答案是否解决了您的问题?想知道是否有任何新的发现与此问题相关。 - Bugfish
1个回答

3
WkWebView和运动加载在Xcode 9与iOS 11版本中可以使用,但在iOS 12中似乎不再起作用。您说的对,WebKit在第二张图片上崩溃了。
由于您是Swift的新手,我建议您阅读此链接以了解委托,因为我提供的解决方案将更加易于理解。 Swift Delegates 简而言之,“委托是一种设计模式,允许一个对象在特定事件发生时向另一个对象发送消息。”
通过这个解决方案/技巧,我们将使用几个WKNavigationDelegates来通知我们WkWebView正在执行特定任务并注入解决方案。您可以在此处找到WKWebKit具有的所有委托 WKNavigationDelegates
下面的代码可以在全新的iOS项目中使用,并替换ViewController.swift中的代码。它不需要任何接口生成器或IBOutlet连接。它将在视图上创建一个单独的Web视图,并指向地址192.168.2.75:6789。我已经包含了内联注释,以尝试解释代码正在做什么。
  1. We are loading the HTTP Response twice from motion in decidePolicyFor navigationResponse delegate and keeping track with a counter. I have left some print statements so you can see what the response is. The first is a header and the second is the image information.
  2. When our counter gets to 3 items (ie the second image) we are forcing the wkWebView to cancel all navigation (ie stop loading) in the decidePolicyFor navigationResponse delegate. See the line with decisionHandler(.cancel). This is what is stopping the crash.
  3. This leads us to receive the callback from the wkwebview delegate WebView didFail navigation. At this point we want load our motion/pi url again and start the loading process again.
  4. We must then reset our counter so we can repeat this process until someone else comes up with a better solution.

    import UIKit
    import WebKit
    
    class ViewController: UIViewController, WKNavigationDelegate  {
    
        // Memeber variables
        var m_responseCount = 0; /* Counter to keep track of how many loads the webview has done.
                        this is a complete hack to get around the webkit crashing on
                        the second image load */
        let m_urlRequest = URLRequest(url: URL(string: "http://192.168.2.75:6789")!) //Enter your pi ip:motionPort
        var m_webView:WKWebView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            m_webView = WKWebView(frame: self.view.frame)  // Create our webview the same size as the viewcontroller
            m_webView.navigationDelegate = self            // Subscribe to the webview navigation delegate
        }
    
        override func viewDidAppear(_ animated: Bool) {
            m_webView.load(m_urlRequest)                    // Load our first request
            self.view.addSubview(m_webView)                 // Add our webview to the view controller view so we can see it
        }
    
        // MARK: - WKNavigation Delegates
        func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            print("decidePolicyFor navigationAction")
            print(navigationAction.request) //This is the request to connect to the motion/pi server http::/192.168.2.75:6789
            decisionHandler(.allow)
        }
    
        func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
            print("decidePolicyFor navigationResponse")
            print(navigationResponse.response) // This is HTML from the motion/rpi
    
        /* We only want to load the html header and the first image
           Loading the second image is causing the crash
             m_responseCount = 0 - Header
             m_responseCount = 1 - First Image
             m_responseCount >= 2 - Second Image
        */
            if(m_responseCount < 2)
            {
                decisionHandler(.allow)
            }
            else{
                decisionHandler(.cancel) // This leads to webView::didFail Navigation Delegate to be called
            }
    
            m_responseCount += 1;  // Incriment our counter
    
        }
    
        func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        /*
         We have forced this failure in webView decidePolicyFor navigationResponse
         by setting decisionHandler(.cancel)
         */
            print("didFail navigation")
    
            m_webView.load(m_urlRequest) //Lets load our webview again
            m_responseCount = 0     /*  We need to reset our counter so we can load the next header and image again
                                    repeating the process forever
                                */
        }
    
        func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
        // If you find your wkwebview is still crashing break here for
        // a stack trace
            print("webViewWebContentProcessDidTerminate")
    
        }
    }
    
注意:由于motion/pi服务器响应为http而不是https,您还需要将以下内容添加到您的info.plist文件中。
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoadsInWebContent</key>
    <true/>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

我建议您使用这个基本示例并对其进行修改以满足您的应用程序要求。我还鼓励您发布任何自己的发现,因为我正在使用与您完全相同的硬件遇到完全相同的问题,这只是一种解决方案而不是真正的解决方案。


NSAllowsArbitraryLoads together with NSAllowsArbitraryLoadsInWebContent does not make sense, since the last overrides the first, it it the same as defining only NSAllowsArbitraryLoadsInWebContent - Nikita

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接