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

如何在ApplicationWillTerminate中执行JS回调?

问题解决:应用终止时通知WKWebView网页

核心原因

applicationWillTerminate的执行时间窗口极短,而WKWebView.evaluateJavaScript是异步操作——系统很可能在JS回调的闭包执行完成前就终止了应用进程,导致网页无法收到通知。

尝试修复:延长执行时间窗口

可以通过后台任务或调度组等待的方式,尝试争取一点时间让JS调用完成,但这种方法不保证100%可靠,因为iOS对应用终止前的执行时间限制很严格。

方案1:使用后台任务延长执行时间

func applicationWillTerminate(_ application: UIApplication) {
    guard let callback = unloadListenerCallback, !callback.isEmpty else {
        debugPrint("got null callback - stop listening")
        unloadListenerCallback = nil
        return
    }
    
    // 申请短暂后台任务
    var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
    backgroundTaskID = application.beginBackgroundTask(withName: "NotifyWebViewAppTerminate") {
        application.endBackgroundTask(backgroundTaskID)
        backgroundTaskID = .invalid
    }
    
    // 执行JS回调
    HomeController.instance.webView?.evaluateJavaScript(callback) { [weak self] result, error in
        defer {
            // 结束后台任务
            if backgroundTaskID != .invalid {
                application.endBackgroundTask(backgroundTaskID)
                backgroundTaskID = .invalid
            }
        }
        
        if let error = error {
            print("Error executing terminate callback: \(error), callback: \(callback)")
        } else {
            print("Executed terminate callback: \(callback), result: \(result ?? "nil")")
        }
        self?.unloadListenerCallback = nil
    }
}

方案2:使用调度组等待回调完成

func applicationWillTerminate(_ application: UIApplication) {
    guard let callback = unloadListenerCallback, !callback.isEmpty else {
        debugPrint("got null callback - stop listening")
        unloadListenerCallback = nil
        return
    }
    
    let dispatchGroup = DispatchGroup()
    dispatchGroup.enter()
    
    HomeController.instance.webView?.evaluateJavaScript(callback) { [weak self] result, error in
        defer { dispatchGroup.leave() }
        
        if let error = error {
            print("Error executing terminate callback: \(error), callback: \(callback)")
        } else {
            print("Executed terminate callback: \(callback), result: \(result ?? "nil")")
        }
        self?.unloadListenerCallback = nil
    }
    
    // 最多等待0.5秒,避免系统强制终止应用
    _ = dispatchGroup.wait(timeout: .now() + 0.5)
}

可靠替代方案

如果上述修复仍无法生效,建议换用更可靠的间接通知方式:

1. 后端状态同步

  • 应用进入后台时,向后端接口发送"后台状态"标记;
  • 应用正常退出时(如通过applicationWillTerminate),发送"已终止"标记;
  • 网页通过轮询后端接口或WebSocket长连接,获取应用的实时状态;
  • 若后端长时间未收到应用的心跳/状态更新,自动判定应用已终止,网页查询时即可知晓。

2. 网页端主动检测

  • 网页定时向原生应用发送心跳请求(通过window.webkit.messageHandlers调用原生方法);
  • 若原生在指定时间内未响应,网页判定应用已终止;
  • 同时,网页可监听pagehide事件,当WebView被销毁时触发该事件,作为应用终止的辅助判断。

3. 本地存储标记(适用于下次加载时知晓)

  • 在applicationWillTerminate中尝试写入网页的本地存储:
    HomeController.instance.webView?.evaluateJavaScript("localStorage.setItem('appWasTerminated', 'true')") { _, _ in }
    
  • 网页在加载时读取该标记:localStorage.getItem('appWasTerminated'),即可知晓上次应用是异常终止;
  • 注意:此方法无法实时通知,仅适用于下次网页加载时回溯状态。

内容的提问来源于stack exchange,提问作者Andrey Lesnykh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 07:45:38