我对iOS状态管理的理解是,当用户点击主页按钮时,应用程序变为非活动状态,然后进入后台,然后暂停几秒钟。如果系统需要释放内存或者用户将应用程序从最近列表中移开,则会终止暂停的应用程序。
我的问题是,有没有办法告诉我我的应用已离开后台状态并进入暂停状态?我了解应用委托方法,例如{{ 1}}等,但有没有办法可以告诉应用程序被暂停?我是否认为暂停不与终止相同?
我提出这个问题的背景是我正在创建一个音频播放器应用。我已经在applicationDidEnterBackground
中启用了背景音频,所以当播放音频时我按下主页按钮我可以看到应用程序无限期地保留在后台(这很好)。但是,当音频没有播放时,不需要将应用程序保留在后台,据我所知,该应用程序应该暂停。我希望能够检查这是否正在发生!
非常感谢 - 并纠正我的任何误解。
答案 0 :(得分:3)
iOS不会报告应用何时将被挂起(从后台置于非处理状态),也不会在应用恢复后触发通知。对此有些困惑,因为当应用程序变为“活动”状态或将退出“活动”状态时会发出通知,但这并不总是所需的正确值。 iOS应用具有多种状态:
为了捕获何时暂停应用程序,您必须捕获何时将应用程序置于后台,并使用backgroundTimeRemaining
之类的值来估计何时暂停。实际的暂停只能计算为运行循环处理中的“间隔”,可以使用计划的循环计时器和减法来完成。我为此创建了一个帮助器类:
https://gist.github.com/BadPirate/0a480b947744c8c0e326daa4ab479b09
import UIKit
import Foundation
internal extension Notification.Name {
static let applicationWillSuspend = Notification.Name("application-will-suspend")
/// This notification gets called after the fact, but the `object` parameter is set to the `Date` of when the suspend occurred
static let applicationDidSuspend = Notification.Name("application-did-suspend")
static let applicationDidUnsuspend = Notification.Name("application-did-unsuspend")
static let suspendStatusRecorderFailed = Notification.Name("suspend-status-recorder-failed")
}
internal class SuspendStatusRecorder {
private var timer : Timer?
private var task : UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier.invalid
/// Start monitoring for suspend
/// - parameter stallThreshold: Number of seconds of no processing before reporting a stall event
internal func start() {
stop() // If already going.
startTask()
let timer = Timer(timeInterval: 1, repeats: true) { [weak self] (_) in
self?.checkStatus()
}
RunLoop.main.add(timer, forMode: .common)
}
internal func stop() {
if let timer = timer {
timer.invalidate()
self.timer = nil
}
endTask()
}
private var lastPing : Int = 0
private func willExpire() {
endTask() // Allow app to suspend
NotificationCenter.default.post(name: .applicationWillSuspend, object: nil)
expectingSuspend = true
}
/// Set to an uptime value for when we expect our app to be suspended based on backgroundTimeRemaining
private var expectingSuspend = false
private func checkStatus() {
let ping = uptime()
if expectingSuspend {
if ping - lastPing > 3 ||
UIApplication.shared.applicationState == .active
{
// Timer stalled, either CPU failure or we were suspended.
NotificationCenter.default.post(name: .applicationDidSuspend, object: Date(timeIntervalSinceNow: TimeInterval(lastPing - ping)))
NotificationCenter.default.post(name: .applicationDidUnsuspend, object: nil)
expectingSuspend = false
startTask() // New background task so that we can make sure to catch next event
}
}
lastPing = uptime()
// In background, time is going to expire (resulting in suspend), report and end task
if UIApplication.shared.applicationState == .background &&
UIApplication.shared.backgroundTimeRemaining != Double.greatestFiniteMagnitude &&
task != UIBackgroundTaskIdentifier.invalid
{
willExpire()
}
}
private func endTask() {
if task != UIBackgroundTaskIdentifier.invalid {
UIApplication.shared.endBackgroundTask(task)
self.task = UIBackgroundTaskIdentifier.invalid
}
}
private func startTask() {
task = UIApplication.shared.beginBackgroundTask(expirationHandler: { [weak self] in
self?.willExpire()
})
}
private func uptime() -> Int {
var uptime = timespec()
if 0 != clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) {
NotificationCenter.default.post(name: .suspendStatusRecorderFailed, object: "Could not execute clock_gettime, errno: \(errno)")
stop()
}
return uptime.tv_sec
}
deinit {
stop()
}
}
答案 1 :(得分:2)
您没有收到有关被暂停的通知:
“暂停:应用程序在后台但没有执行代码。系统会自动将应用程序移动到此状态,并且在执行此操作之前不会通知它们。暂停时,应用程序仍保留在内存中但不执行任何代码。
当内存不足时,系统可能会清除已暂停的应用程序,恕不另行通知,以便为前台应用程序腾出更多空间。“
答案 2 :(得分:1)
您可以使用后台任务来检查应用是否已暂停。
SuspensionObserver
之类的帮助器类。SuspensionObserver
内,您应该有一个类似wasAppSuspended
的标志。func observe()
内创建UIApplication.shared.beginBackgroundTask
。 wasAppSuspended
内将false
标志设置为observe()
wasAppSuspended
内将true
标志设置为expirationHandler
。 observe()
或didFinishLaunchingWithOptions
呼叫applicationDidEnterBackground
。applicationWillEnterForeground
中,您可以检查该应用是否已暂停。此外,您还可以将委托/处理程序设置为在应用暂停之前执行所需的操作。