You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS中WKWebView非活跃时setTimeout失效问题及解决方案咨询

WKWebView非活跃状态下保持定时任务运行的解决方案

问题背景

当从A WebViewController跳转(push)至B WebViewController时,A WebViewController中WKWebView的setTimeout/setInterval会停止运行,返回A后才恢复;而PC端浏览器切换标签页不会影响这类定时任务的执行,这是iOS WKWebView特有的行为。

原因解释

这是苹果的系统资源优化机制:当WKWebView所在的视图控制器脱离屏幕(处于导航栈下层)时,其JavaScript运行线程会被系统挂起,暂停所有事件循环相关的任务,包括定时回调。PC端浏览器对后台标签页的JS线程采用低优先级运行策略,不会完全暂停,因此不存在这个问题。苹果官方文档明确提到,WKWebView会根据视图的活跃状态调整JS线程的运行状态,以节省CPU和内存资源。

可行解决方案

方案一:Native层接管定时逻辑(推荐)

将JS中的定时任务迁移到iOS Native层,利用不受视图活跃状态影响的Native定时器(GCD/Timer),定时调用WKWebView的evaluateJavaScript方法触发JS业务逻辑。

修改后的示例代码

class DemoVC: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .lightGray
    }
}

class ViewController: UIViewController {
    private lazy var webView = { () -> WKWebView in
        let webViewConfiguration = WKWebViewConfiguration()
        let userContentController = WKUserContentController()
        userContentController.add(self, name: "logHandler")
        webViewConfiguration.userContentController = userContentController

        let webView = WKWebView(frame: .zero, configuration: webViewConfiguration)
        let script = """
        (function() {
            var oldLog = console.log;
            console.log = function(message) {
                oldLog(message);
                window.webkit.messageHandlers.logHandler.postMessage(message);
            };
            // 暴露供Native调用的JS方法
            window.executeLog = function() {
                console.log("1");
            };
        })();
        """
        webView.configuration.userContentController.addUserScript(WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: false))

        let htmlString = """
        <!DOCTYPE html>
        <html>
        <head>
            <title>WKWebView Test</title>
        </head>
        <body>
            <h1>Testing JavaScript in WKWebView</h1>
        </body>
        </html>
        """
        webView.loadHTMLString(htmlString, baseURL: nil)

        return webView
    }()

    private lazy var button = { () -> UIButton in
        let button = UIButton(frame: .zero)
        button.setTitle("push", for: .normal)
        button.setTitleColor(.black, for: .normal)
        button.addTarget(self, action: #selector(pushAction), for: .touchUpInside)
        return button
    }()

    // GCD定时器
    private var timer: DispatchSourceTimer?

    override func viewDidLoad() {
        super.viewDidLoad()

        webView.frame = view.bounds
        view.addSubview(webView)

        view.addSubview(button)
        button.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
        button.center = webView.center

        // 初始化定时器
        setupTimer()
    }

    private func setupTimer() {
        timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
        timer?.schedule(deadline: .now(), repeating: .seconds(3))
        timer?.setEventHandler { [weak self] in
            DispatchQueue.main.async {
                self?.webView.evaluateJavaScript("executeLog()") { _, error in
                    if let error = error {
                        print("JS调用失败: \(error)")
                    }
                }
            }
        }
        timer?.resume()
    }

    @objc private func pushAction() {
        navigationController?.pushViewController(DemoVC(), animated: true)
    }

    deinit {
        timer?.cancel()
    }
}

extension ViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "logHandler", let log = message.body as? String {
            print("JavaScript log: \(log)")
        }
    }
}

代码说明

  1. 在JS中新增executeLog方法,封装原有的定时逻辑;
  2. Native层使用GCD定时器,每隔3秒调用evaluateJavaScript执行executeLog();
  3. 定时器在视图控制器初始化时启动,销毁时取消,避免内存泄漏;
  4. 即使push到新页面,Native定时器仍会正常运行,持续触发JS代码执行。

方案二:共享WKProcessPool(不推荐)

将WKWebView的WKProcessPool设置为全局共享实例,理论上可以让JS线程在后台保持运行,但该方法不稳定:系统仍可能根据资源占用情况挂起线程,且会增加APP的内存消耗,仅在特殊场景下尝试。

内容的提问来源于stack exchange,提问作者Crystal Black

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 17:53:17