升级 iOS 13.2 后应用在后台频繁被杀给大量用户带来困扰。网络上wakeup 分析日志流传甚广,也有人断定因为应用不遵守 iOS 后台唤醒规则,所以被杀。
确定的是 wakeup 调用不是 App 被杀掉的直接原因,不能简单归因后台频繁被杀是开发者的问题。现在 Apple 提供了 MetricKit API 可以从系统层面获取应用后台被杀的原因,并给出了一些改善建议。
系统给出的应用程序后台终止的主要原因 :
新的MetricKit API 提供后台被杀的原因
MXBackgroundExitData
通过提供每次应用程序被终止时的退出计数,了解为什么应用程序会被杀死。
崩溃是最直接的终止类型。可能发生的原因有以下3种
这些事件将在crashlog上生成,并自动向我们报告。除了Xcode organizer之外,MetricKit还为每个设备增加了更多的API,即 "MXCrashDiagnostic"。
MXCrashDiagnostic
将提供以下信息
另一类被杀是由于Watchdog事件而发生的,该事件发生在一些关键的过程中的超时。
CPU资源限制是指后台CPU持续负载较高。 在Xcode 12中增加了一些解决方案。
MXCPUExceptionDiagnostic
实现电量异常报告。BGProcessingTask
。应用程序占用太多内存。一些解决方案 :
注意:这不是你的应用程序的错误,它是最常见的退出原因。发生这种情况是因为系统为活动的应用程序腾出内存。
如何降低内存压力退出率?
建议在后台做以下操作。
还有关于如何从内存压力退出中恢复的建议。
当进入后台时,我们可以使用 "beginBackgroundTask "和 "endBackgroundTask "来执行后台任务(系统会给你30秒的时间来完成任务)
UIApplication.beginBackgroundTask(expertationHandler:) UIApplication.endBackgroundTask(_:) 复制代码
给BackgroundTask命名来排查没有结束后台任务的问题。
UIApplication.beginBackgroundTask(withName:experienceHandler:) 复制代码
为什么?
另一个解决办法是使用expirationHandler
。
expirationHandler
作为保障措施,不要完全依赖它。endBackgroundTask
。let handle = MXMetricManager.makeLogHandle(category: "DatabaseExpHandler") mxSignpost(.event, log: handle, name: "Entered") cancelOperations() closeDatabase() mxSignpost(.event, log: handle, name: "Exited") UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier) 复制代码
让我们检查 "MXMetricPayload",看看标记数量,并检查标记数量是否有不平衡(非成对出现)。
另一种改善应用程序终止调试的解决方案是在做一些后台工作之前检查backgroundTimeRemaining
。
示例代码 :
let minimumTimeRemaining = min(5, estimateProcessingTime(inputData)) if UIApplication.shared.backgroundTimeRemaining > minimumTimeRemaining { // 剩余时间足够,调用开始后台任务 return UIApplication.shared.beginBackgroundTask { ... } }else { // 时间不够,将这项工作推迟到以后进行。 registerProcessingTask(inputData) return .invalid } 复制代码
下一步需要避免内存泄漏UIBackgroundTaskIdentifier
。使用局部变量而不是实例变量来保存UIBackgroundTaskIdentifier
,这样可以防止内存泄漏,因为它将在不同的内存上分配。
示例代码 :
@IBAction func beginDataExport(sender: UIButton) { var taskId.UIBbackgroundTaskIdentifier = .invalid: UIBbackgroundTaskIdentifier = .invalid taskId = UIApplication.shared.beginBackgroundTask {...}。 //归档后结束后台任务,这需要几秒钟的时间。 ArchiveUtility.exportUserData(completion: ()->()) { UIApplication.shared.endBackgroundTask(taskId) } } 复制代码
wwdc2020/10078 为什么我的应用程序被杀死? developer.apple.com/videos/play…
wwdc2020/10081 MetricKit的新功能: developer.apple.com/videos/play…
MetricKit API 后台应用程序退出计数文档: developer.apple.com/documentati…
MetricKit 及它的使用方式,也提供了一个收集 MetricKit 数据的自建 Web 服务方案: nshipster.com/metrickit/
UI状态恢复文档: developer.apple.com/documentati…