应用终止时Live Activity无法稳定关闭的问题求助
我太懂这种时灵时不灵的情况有多闹心了!你现在的代码逻辑上看似没问题,但架不住iOS系统在App终止阶段的“暴脾气”——applicationWillTerminate这个方法的执行时间窗口特别窄,系统不会无限等待你的异步任务完成,很可能你还在等信号量的时候,系统就已经把App进程干掉了,这就是为什么有时候成功有时候失败。
先给你拆解下当前代码的核心风险点:
- 用
semaphore.wait()阻塞主线程,这在App终止阶段是非常危险的操作,系统看到主线程被卡,可能直接跳过等待强制终止App,导致你的Live Activity关闭逻辑根本没跑完。 - 你是串行遍历每个Activity并调用
await activity.end,如果有多个Live Activity,这个过程的总耗时会拉长,进一步增加被系统打断的概率。
给你几个针对性的改进方案,亲测能大幅提升稳定性:
1. 用后台任务申请额外执行时间,别阻塞主线程
iOS允许我们在需要完成收尾操作时申请后台任务,明确告诉系统“我还有点活没干完,再给我点时间”,这样比阻塞主线程靠谱多了。修改后的代码示例:
func applicationWillTerminate(_ application: UIApplication) { var backgroundTask: UIBackgroundTaskIdentifier = .invalid // 申请后台任务 backgroundTask = application.beginBackgroundTask(withName: "CleanupLiveActivities") { // 超时回调:系统要终止我们了,赶紧结束后台任务 application.endBackgroundTask(backgroundTask) backgroundTask = .invalid } Task.detached(priority: .userInitiated) { defer { // 不管成功失败,最后都要结束后台任务 if backgroundTask != .invalid { application.endBackgroundTask(backgroundTask) backgroundTask = .invalid } } print("开始关闭所有Live Activity...") // 并行处理所有Activity的关闭,大幅缩短总耗时 let closeTasks = Activity<BackToTheAppAttributes>.activities.map { activity in Task { print("正在关闭Live Activity: \(activity.id)") await activity.end(dismissalPolicy: .immediate) } } // 等待所有并行任务完成 await Task.waitForAll(closeTasks) } }
2. 补充后台场景的兜底处理
别忘了,有些杀App的场景(比如从多任务界面划掉)可能先触发applicationDidEnterBackground,再触发applicationWillTerminate;甚至在系统低内存杀App时,applicationWillTerminate可能根本不会被调用。所以可以在applicationDidEnterBackground里也加一层检查,提前关闭不需要在后台保留的Live Activity:
func applicationDidEnterBackground(_ application: UIApplication) { // 这里可以根据业务逻辑,关闭一些不需要在后台保留的Live Activity Task.detached { // 先快照当前活跃的Activity,避免遍历中集合变化 let currentActivities = Activity<BackToTheAppAttributes>.activities for activity in currentActivities { await activity.end(dismissalPolicy: .immediate) } } }
3. 优化Live Activity的销毁策略
如果你的Live Activity不需要在App完全退出后继续显示,可以在创建的时候就设置更合理的dismissalPolicy,比如:
// 示例:创建时设置5秒后自动销毁(可根据业务调整) let activity = try Activity<BackToTheAppAttributes>.request( attributes: attributes, contentState: initialState, dismissalPolicy: .after(Date().addingTimeInterval(5)) )
这样就算系统没触发applicationWillTerminate,Live Activity也会在超时后自动消失,作为兜底保障。
另外还要注意:遍历Activity.activities时最好先存一份快照再处理,避免遍历过程中集合状态变化导致的异常,就像上面代码里的let currentActivities = Activity<BackToTheAppAttributes>.activities这样。
内容来源于stack exchange

