改善和解决应用后台频繁被杀的问题

升级 iOS 13.2 后应用在后台频繁被杀给大量用户带来困扰。网络上wakeup 分析日志流传甚广,也有人判定由于应用不遵照 iOS 后台唤醒规则,因此被杀。swift

肯定的是 wakeup 调用不是 App 被杀掉的直接缘由,不能简单归因后台频繁被杀是开发者的问题。如今 Apple 提供了 MetricKit API 能够从系统层面获取应用后台被杀的缘由,并给出了一些改善建议。xcode

系统给出的应用程序后台终止的主要缘由 :缓存

  • 崩溃
  • CPU资源限制
  • Watchdog
  • 内存资源限制
  • 内存压力退出
  • 后台任务超时

新的MetricKit API 提供后台被杀的缘由安全

MXBackgroundExitData经过提供每次应用程序被终止时的退出计数,了解为何应用程序会被杀死。网络

Crashes

崩溃是最直接的终止类型。可能发生的缘由有如下3种app

  • SIGSEGV
  • SIGILL
  • 断言和异常退出

这些事件将在crashlog上生成,并自动向咱们报告。除了Xcode organizer以外,MetricKit还为每一个设备增长了更多的API,即 "MXCrashDiagnostic"。ide

MXCrashDiagnostic将提供如下信息post

  • 堆栈跟踪
  • 信号
  • 异常代码
  • 终止缘由

Watchdog

另外一类被杀是因为Watchdog事件而发生的,该事件发生在一些关键的过程当中的超时。ui

  • 关键过程当中的超时:在应用程序的关键过程当中,如启动、进入后台或再次进入前台时,出现长时间的挂起。它的时间限制在20秒左右。
  • 在模拟器和调试器中禁用。
  • 修正Watchdog事件将有助于消除死锁、死循环和主线程上频繁的同步工做。
  • "MXCrashDiagnostic"中提供报告。

CPU资源限制

CPU资源限制是指后台CPU持续负载较高。 在Xcode 12中增长了一些解决方案。spa

  • 经过xcode organizer和MXCPUExceptionDiagnostic实现电量异常报告。
  • 调用堆栈指出代码中的频繁调用。
  • 考虑将工做转入BGProcessingTask

内存占用超标

应用程序占用太多内存。一些解决方案 :

  • 前景和背景的限制相同
  • 使用 Instruments 和Memory Debugger
  • 请注意对旧设备的限制

Jetsam (内存压力退出)

注意:这不是你的应用程序的错误,它是最多见的退出缘由。发生这种状况是由于系统为活动的应用程序腾出内存。

如何下降内存压力退出率?

  • 争取在后台使用少于50MB的空间。

建议在后台作如下操做。

  • 将状态保存到磁盘
  • 清空image view
  • 删除缓存

还有关于如何从内存压力退出中恢复的建议。

  • 在进入后台时保存状态,如视图控制器堆栈、text fields中的草稿输入、媒体播放位置以及更多取决于你的业务场景。
  • 使用UIKit状态恢复
  • 想办法让用户意识不到应用程序被终止了。

后台任务超时

当进入后台时,咱们可使用 "beginBackgroundTask "和 "endBackgroundTask "来执行后台任务(系统会给你30秒的时间来完成任务)

UIApplication.beginBackgroundTask(expertationHandler:)
UIApplication.endBackgroundTask(_:)
复制代码
  • 问题是当咱们不调用 "endBackgroundTask"时,会由于没法明确结束任务而致使程序被杀死。
  • 经过 "MXBackgroundExitData "暴露的计数。

给BackgroundTask命名来排查没有结束后台任务的问题。

UIApplication.beginBackgroundTask(withName:experienceHandler:)
复制代码

为何?

  • 在debugger中不会出现终止的状况。
  • 执行控制台消息并审核匹配的后台和结束任务过程的调用

另外一个解决办法是使用expirationHandler

  • 实施一个expirationHandler做为保障措施,不要彻底依赖它。
  • 在处理程序内调用endBackgroundTask
  • 不要在处理程序内开始新的业务
  • BackgroundTask开始时和期满处理程序中增长数据采集。
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

  • 只有在时间充裕的状况下才开始工做 *在剩余时间小于5秒时开始任务是不安全的。

示例代码 :

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)
    }
}
复制代码

总结减小后台exit的解决方案

  • 识别并解决exit问题
  • 减小内存使用量
  • 实施UI状态恢复

参考资料:

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…

本文由你的关注/点赞/评论赞助发表

相关文章
相关标签/搜索