Suspended — iOS 后台应用冻结是什么

作者: IT Sectr 发布日期: 2026-03-03 阅读时间: 11 分钟

Suspended — iOS 应用程序生命周期中的暂停状态,应用程序被冻结在内存中,但不执行任何代码。我们将展示 Suspended 如何工作,后台应用冻结带来哪些风险,iOS 如何管理已挂起应用程序的卸载,以及如何实现状态恢复以在从 Suspended 返回后实现无缝恢复。

要点

  • Suspended — 应用程序在内存中被冻结,代码不执行,UI 堆栈已保存
  • iOS Suspended — Android 中没有的独特状态;进程存在但不活跃
  • 卸载 — 内存不足时,Suspended 应用程序首先被移除,内存中的数据会丢失
  • State Restoration — iOS 用于在卸载后保存和恢复 UI 堆栈的机制
  • didEnterBackground — 进入 Suspended 前保证被调用的最后一个方法

Suspended — 这是什么状态

Suspended 是 iOS 应用程序生命周期中的一个状态,应用程序位于设备的 RAM 中,但不执行任何代码。这是完全终止前的最终状态:应用程序在完成所有后台任务或超时后从 Background 进入 Suspended。在 Suspended 状态下,应用程序被完全冻结 — 所有线程暂停,定时器不工作,没有网络活动。

Suspended 是 iOS 的独特特性,在标准的 Android 生命周期中不存在。原因在于不同的进程管理架构。iOS 将应用程序镜像保存在内存中(类似于桌面系统的休眠),以便用户返回时无需冷启动即可立即恢复界面。Android 没有 Suspended — 进程要么存在并可以执行代码(Background),要么已终止(Not Running),尽管 Android 可以通过 LMK 暂停线程的执行。

对用户而言,Suspended 看起来像是即时恢复:用户通过 App Switcher 在应用间切换,每个应用都从离开的位置打开。这造成所有应用同时运行的假象。实际上,大多数应用都被冻结在 Suspended 状态。从 Suspended 热启动比从 Not Running 冷启动快得多,因为代码已经加载到内存中。

系统如何管理 Suspended

iOS 监控所有应用的状态,并根据可用内存决定卸载 Suspended 应用。当内存不足时,系统开始卸载 Suspended 应用,从处于该状态最久的应用开始。如果内存仍然不足,系统将应用从 Background 和 Inactive 转移到 Suspended 状态,然后卸载。这个过程对用户完全透明 — 用户只会看到 App Switcher 中的应用图标,点击时会触发冷启动。

特性Suspended (iOS)Background (iOS)Background (Android)
代码执行是(有限)是(有限)
在内存中
CPU 消耗0%
热启动是 — 即时恢复是 — 通过 Inactive否 — 进程可能已被终止
超时无 — 可在内存中停留数小时约 30 秒(beginBackgroundTask 后)取决于 API 版本
系统卸载内存不足时内存严重不足时LMK(低内存杀手)
恢复运行从 App Switcher — 即时从 App Switcher — 通过 Inactive冷启动
状态恢复推荐不需要SavedStateHandle

iOS 中的 Suspended:冻结机制

在 iOS 中,Suspended 在所有后台任务完成后自动进入。系统调用 applicationDidEnterBackground,给予执行 beginBackgroundTask 的时间(约 30 秒),然后强制暂停所有线程并将应用程序转入 Suspended 状态。此时内存中的对象被保留,但不执行任何代码 — 应用程序保持在当前状态被冻结。

关键点:applicationDidEnterBackground 是进入 Suspended 前保证被调用的最后一个方法。之后应用程序不会收到任何关于内存卸载的通知。如果用户或系统终止处于 Suspended 状态的应用程序,既不会调用 applicationWillTerminate,也不会再次调用 applicationDidEnterBackground。因此所有数据保存必须在 applicationDidEnterBackground 中完成,而不是在 applicationWillTerminate 中。

swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    // 进入 Suspended 前的最后保证调用
    func applicationDidEnterBackground(_ application: UIApplication) {
        // 保存所有需要在内存卸载后保留的数据
        savePersistentState()
        saveNavigationStack()

        // 如果需要,请求额外时间
        let task = application.beginBackgroundTask {
            application.endBackgroundTask(task)
        }
    }

    // 从 Suspended 返回 — 热启动
    func applicationWillEnterForeground(_ application: UIApplication) {
        // 应用程序已在 Suspended 状态,恢复运行
        print("从 Suspended 或 Background 返回")
    }

    // 内存卸载后的完全恢复
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // 如果这是从 Suspended 卸载后的冷启动 —
        // 恢复状态恢复
        return true
    }

    private func savePersistentState() {
        UserDefaults.standard.set(Date(), forKey: "lastActiveDate")
    }

    private func saveNavigationStack() {
        guard let rootVC = window?.rootViewController else { return }
        // 保存当前导航堆栈
        if let navController = rootVC as? UINavigationController {
            let vcClasses = navController.viewControllers.map { type(of: $0) }
            UserDefaults.standard.set(vcClasses.map { NSStringFromClass($0) }, forKey: "navStack")
        }
    }
}

代码中展示了 iOS 上处理 Suspended 的关键逻辑。applicationDidEnterBackground 是最后保证的调用。所有数据保存必须在此处完成:用户状态、导航堆栈、草稿、定时器。applicationWillEnterForeground 在从 Suspended 或 Background 返回时调用。didFinishLaunchingWithOptions — 仅在冷启动时调用,即应用程序在 Suspended 后从内存中卸载时。

Android 中的 Suspended — 是否存在类似状态

Android 没有直接对应的 iOS Suspended 状态。Android 不会冻结内存中的应用程序并保留执行上下文。相反,Android 要么将进程保留在后台(Background),要么终止它(Not Running)。但在 Android 11+(API 30)上引入了 App Freezer 机制,通过 SIGSTOP 信号暂停后台进程的执行。这是 Suspended 的功能性类似状态,但存在重要区别。

App Freezer 是 Android 内存管理系统的一部分。当应用程序长时间处于后台且没有活跃通知时,系统向其发送 SIGSTOP,暂停所有线程。当应用程序回到前台时,发送 SIGCONT,执行恢复。与 iOS 的关键区别:App Freezer 不保证状态保存 — 如果进程在冻结期间被终止,内存中的数据可能丢失。

在 Android 上,推荐使用 SavedStateHandle 在 ViewModel 中自动保存任何进程终止时的状态。SavedStateHandle 通过 onSaveInstanceState 将数据保存在 Bundle 中,该 Bundle 既能度过 App Freezer 也能度过 Process Death。与 iOS 不同,在 iOS 中从 Suspended 卸载是例外情况,而在 Android 中 Process Death 是正常行为,应始终被预期。

kotlin
// SavedStateHandle — Android 上避免进程终止的救星
class CheckoutViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    // 即使在应用冻结后也能保留的状态
    var currentStep: MutableLiveData<Int> =
        savedStateHandle.getLiveData("checkout_step", 1)

    var cartItems: MutableLiveData<List<CartItem>> =
        savedStateHandle.getLiveData("cart_items", emptyList())

    fun proceedToNextStep() {
        currentStep.value = (currentStep.value ?: 0) + 1
    }

    fun addToCart(item: CartItem) {
        val updatedList = (cartItems.value ?: emptyList()) + item
        cartItems.value = updatedList
        savedStateHandle["cart_items"] = updatedList
    }
}

// 在 onStop 中保存以应对应用冻结
class MainActivity : AppCompatActivity() {

    override fun onStop() {
        super.onStop()
        // 保存需要在冻结后保留的数据
        saveDraftData()
        // 释放在冻结状态下不需要的资源
        releaseHeavyResources()
        // 警告应用程序将被冻结
        // (调试日志记录)
        Log.d("生命周期", "Activity 已停止 — 可能的应用冻结")
    }
}

代码中展示了处理 Android 上 Suspended 类似状态的方法。ViewModel 中的 SavedStateHandle 在 Process Death 时自动保存和恢复数据。onStop 是进入可能的 App Freezer 或进程终止前最后保证的事件。结账表单状态、购物车商品列表 — 所有这些数据都通过 SavedStateHandle 度过冻结。对于重资源(位图、数据库游标),onStop 是释放内存的地方。

状态恢复:从 Suspended 恢复

State Restoration 是 iOS 内置的机制,用于在应用程序从内存中卸载后保存和恢复 UI 状态。如果应用程序处于 Suspended 状态并被系统卸载,下次冷启动时状态恢复会恢复导航堆栈、滚动位置、表单状态和其他 UI 元素。用户将回到离开时的同一界面。

State Restoration 通过 UIViewControllerRestorationUIStateRestoring 协议工作。开发者为每个想要恢复的 ViewController 和 View 分配 restorationIdentifier。在进入 Background 时,iOS 对这些对象的状态进行编码。在卸载后返回时,iOS 创建新对象并解码保存的状态。没有状态恢复,用户在冷启动后会看到空白界面,而不是离开时的位置。

swift
import UIKit

class DetailViewController: UIViewController {

    var itemID: String = ""
    var scrollPosition: CGPoint = .zero

    override func viewDidLoad() {
        super.viewDidLoad()
        restorationIdentifier = "DetailViewController"
        restorationClass = type(of: self)
    }

    override func encodeRestorableState(with coder: NSCoder) {
        super.encodeRestorableState(with: coder)
        coder.encode(itemID, forKey: "itemID")
        coder.encode(scrollPosition, forKey: "scrollPosition")
    }

    override func decodeRestorableState(with coder: NSCoder) {
        super.decodeRestorableState(with: coder)
        if let savedID = coder.decodeObject(forKey: "itemID") as? String {
            itemID = savedID
            loadItem()
        }
        if let savedPosition = coder.decodeCGPoint(forKey: "scrollPosition") {
            scrollPosition = savedPosition
            // 数据加载后恢复位置
        }
    }
}

// AppDelegate — 激活状态恢复
func application(
    _ application: UIApplication,
    shouldSaveSecureApplicationState coder: NSCoder
) -> Bool {
    return true
}

func application(
    _ application: UIApplication,
    shouldRestoreSecureApplicationState coder: NSCoder
) -> Bool {
    return true
}

代码中展示了 iOS 上 State Restoration 的实现。每个可恢复的 ViewController 都需要 restorationIdentifierrestorationClass。encodeRestorableState/decodeRestorableState 通过 NSCoder 保存和加载数据。在 AppDelegate 中,shouldSaveSecureApplicationState 和 shouldRestoreSecureApplicationState 启用加密状态保存。从 iOS 12+ 开始,推荐使用安全编码(NSSecureCoding)来保护数据。

Suspended 最佳实践

第一条规则 — 永远不要指望应用程序会从 Suspended 返回。系统可以在任何时候卸载应用程序。所有关键数据必须在进入 Suspended 之前保存到持久化存储中 — 即在 applicationDidEnterBackground 或 onStop 中。UserDefaults、Core Data、File Manager 是合适的存储方式。内存(变量、属性)对于需要在 Suspended 后保留的数据来说是不可靠的存储方式。

第二条规则 — 在进入 Suspended 前释放资源。关闭文件描述符,释放 GPU 内存(Metal、Core Graphics),关闭网络连接。虽然应用程序在 Suspended 状态下不消耗 CPU,但占用的资源会被阻塞,无法被其他应用程序使用。在 iOS 的 Suspended 状态下不能保持打开的套接字 — 从 Suspended 返回后它们可能无法正常工作,导致错误。

第三条规则 — 不要将依赖时间的逻辑放在等待从 Suspended 返回的位置。定时器、回调和网络活动在 Suspended 状态下会停止。如果应用程序在 Suspended 状态下停留数小时,返回时定时器可能触发不正确。返回时检查数据的时效性 — 缓存可能已过期,授权令牌可能已失效。

第四条规则 — 对所有界面使用 State Restoration,特别是输入表单、可滚动列表和详情界面。没有状态恢复,用户从已卸载的 Suspended 返回后将看到应用程序的初始界面,而不是离开时的位置。这会降低用户体验,迫使用户重复操作。

swift
import UIKit

// 检查:应用程序是否已从内存中卸载?
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    // 检查是否有保存的状态
    if UserDefaults.standard.object(forKey: "navStack") != nil {
        // 应用程序已从 Suspended 卸载
        // 需要恢复状态
        restoreNavigationStack()
    } else {
        // 从 Not Running 的纯冷启动
        showOnboardingIfNeeded()
    }
    return true
}

private func restoreNavigationStack() {
    guard let savedStack = UserDefaults.standard.array(forKey: "navStack") as? [String],
          let navController = window?.rootViewController as? UINavigationController
    else { return }

    for vcClassName in savedStack {
        if let vcClass = NSClassFromString(vcClassName) as? UIViewController.Type {
            let vc = vcClass.init()
            navController.pushViewController(vc, animated: false)
        }
    }
}

代码中展示了判断应用程序是否已从 Suspended 卸载的实践。检查 UserDefaults 中是否存在保存的导航堆栈,可以区分卸载后的冷启动和纯冷启动。在第一种情况下,恢复导航堆栈;在第二种情况下,显示引导页或主界面。这种方法在 NSCoder 不足以处理的情况时补充了内置的 State Restoration。

常见问题

应用程序可以在 Suspended 状态下停留多长时间?

没有限制 — 从几秒到几天。iOS 对 Suspended 没有超时限制。应用程序将保持在内存中,直到系统因资源不足决定卸载它。实际上,应用程序在 Suspended 状态下停留 15 分钟到几个小时,具体取决于设备的 RAM 容量和活跃应用的数量。

从 Suspended 卸载时会调用 applicationWillTerminate 吗?

不会。applicationWillTerminate 不会被调用当应用程序从 Suspended 卸载时。系统只是释放内存,不通知应用程序。这是为什么所有数据保存必须在 applicationDidEnterBackground 中完成的另一个原因。applicationWillTerminate 仅在用户通过从 App Switcher 滑动手动终止应用程序时调用。

Android 有 Suspended 的类似状态吗?

没有直接对应的状态。在 Android 11+ 上引入了 App Freezer,通过 SIGSTOP 暂停后台进程 — 这在功能上类似于 Suspended。但 Android 应用程序必须设计为随时可能发生 进程终止。使用 ViewModel 中的 SavedStateHandle 和 onSaveInstanceState 保存状态,以度过 App Freezer 和进程终止。

如何检查应用程序是否曾处于 Suspended 状态?

在 iOS 中没有直接的 API 可以检查。间接方法:在 didFinishLaunchingWithOptions 中检查 UserDefaults 中是否存在保存的状态。如果状态存在 — 应用程序已从 Suspended 卸载并冷启动。如果状态不存在 — 纯冷启动。在 SwiftUI 中,可以在 scenePhase.background 中保存一个标志,并在下次启动时检查。

在 Suspended 的上下文中什么是快照?

进入 Suspended 时,iOS 会拍摄 快照 — 应用程序当前 UI 的截图。此快照显示在 App Switcher 中,并在返回应用程序时(作为「解冻」动画)显示。如果应用程序包含敏感数据,快照可能会泄露它们。为了保护,请使用 UIApplication.shouldSnapshotSecureApp(iOS 16+)或在 applicationDidEnterBackground 中叠加模糊层。

总结

  • Suspended — 应用程序在 iOS 内存中被冻结,代码不执行,但 UI 堆栈已保存以便即时恢复
  • iOS 独特性 — Android 中没有 Suspended;Android 使用 App Freezer(SIGSTOP)作为部分类似状态
  • 卸载 — 系统在内存不足时首先卸载 Suspended 应用程序,且不发送通知
  • 保存 — applicationDidEnterBackground 是最后保证调用的方法,所有数据必须在此保存
  • State Restoration — 用于在从 Suspended 卸载后自动恢复 UI 的 NSCoder 机制
  • Android 替代方案 — SavedStateHandle + onSaveInstanceState 用于度过进程终止
  • 快照 — iOS 在 Suspended 时拍摄截图;敏感数据需要通过模糊层或安全快照隐藏

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读