如何判断我的应用是否已暂停? [英] How can I tell if my app has suspended?

查看:84
本文介绍了如何判断我的应用是否已暂停?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对iOS状态管理的理解是,当用户按下主屏幕按钮时,该应用程序将变为非活动状态,然后进入后台,然后在几秒钟后被暂停.如果系统需要释放内存或用户将应用程序从最新记录"列表中滑出,则终止了已暂停的应用程序.

My understanding of iOS state management is that when the user hits the home button, the app becomes inactive, then enters the background, and then after a few seconds is suspended. A suspended app is then terminated if the system needs to free memory or if the user swipes the app away from the recents list.

我的问题是,我有什么办法告诉我我的应用已离开后台状态并进入了暂停状态?我知道像 applicationDidEnterBackground这样的应用程序委托方法等,但是有办法告诉我该应用已被暂停吗?我以为被暂停与被终止不同于是正确的吗?

My question is, is there any way for me to tell that my app has left the background state and entered the suspended state? I'm aware of the application delegate methods like applicationDidEnterBackground etc, but is there a way that I can tell the app was suspended? Am I correct in thinking that being suspended is not the same as being terminated?

我问这个问题的背景是我正在创建一个音频播放器应用程序.我已经在 Info.plist 中启用了背景音频,所以当播放音频时,按下主屏幕按钮,我可以看到该应用程序无限期地保留在背景中(很好).但是,当音频不播放时,无需将应用程序保留在后台,据我所知,该应用程序应该暂停.我希望能够检查这是否正在发生!

My context for asking this question is that I'm creating an audio player app. I've enabled background audio in Info.plist and so when audio is playing and I press the home button I can see that the app remains in the background indefinitely (which is good). However, when audio is not playing there's no need to keep the app in the background and, as far as I understand it, the app ought to suspend. I want to be able to check whether or not this is happening!

非常感谢-并纠正我的任何误解.

Thanks very much - and do correct any misunderstandings I have.

推荐答案

iOS不会报告应用何时被挂起(从后台置于非处理状态),也不会在应用一旦被触发时发出通知已恢复.对此有些困惑,因为当应用程序变为活动"状态或将退出活动"状态时会发出通知,但这并不总是所需的正确值.iOS应用有多种状态:

iOS doesn't report when an app will be suspended (placed from background into a non-processing state) nor does it seem to fire a notification once the app has resumed. There is some confusion about this as there is a notification when the app becomes "active" or will resign the "active" state, however this is not always the right value needed. iOS Apps have a number of states:

  • 活动:应用位于最前面(最前面),没有任何通知或菜单被拉到上方.下拉菜单或获取外部通知或短信将使该应用程序退出"活动状态,并在处理警报后恢复活动状态.
  • 背景:应用程序不在前台,但仍在处理中.如果没有后台任务在运行,则会在挂起之前短暂发生;如果长时间运行的后台模式(音频,位置等)正在运行,则此状态可能会变为永久状态.
  • 已暂停:应用程序在内存中,但运行循环和处理已暂停
  • 已终止:应用未运行或在内存中,已被用户(强制退出)或操作系统(以释放内存)退出,或者从未启动
  • Active: App is in the foreground (frontmost) and there are no notifications or menu's pulled over it. Pulling a menu down or getting an external notification or text message will cause the app to "resign" active, and resume active once the alert has been dealt with.
  • Background: App is not in the foreground but still processing. This happens briefly before suspend if there are no background tasks running, or can be a permanent state if there is a long running background mode (audio, location, etc) running.
  • Suspended: App is in memory, but run loops and processing is paused
  • Terminated: App is not running or in memory, has been quit either by user (force quit) or OS (to free up memory) or was never launched

为了捕获何时暂停应用程序,您必须捕获何时将应用程序置于后台,并使用 backgroundTimeRemaining 之类的值来估算何时将暂停.实际的暂停只能计算为运行循环处理中的间隙",这可以使用计划的循环计时器和减法来完成.我为此创建了一个帮助器类:

In order to capture when app is suspended, you'll have to capture when app is being put into background and use values like backgroundTimeRemaining to estimate when a suspension will occur. Actual suspension can only be calculated as a "gap" in run loop processing, which can be done with a scheduled recurring timer and subtraction. I've created a helper class for this:

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()
    }
}

这篇关于如何判断我的应用是否已暂停?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆