改善和解決應用後臺頻繁被殺的問題

升級 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…

本文由你的關注/點贊/評論贊助發表

相關文章
相關標籤/搜索