Inactive — 本质,Active 和 Background 之间的过渡状态

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

Inactive — 应用程序生命周期中 Active 和 Background 之间的过渡状态,在此状态下应用程序在屏幕上可见,但不会收到触摸事件。我们将介绍 Inactive 在 iOS 和 Android 上如何产生,哪些委托方法负责处理它,以及如何正确处理中断——来电、通知和系统手势。

要点总结

  • Inactive — 应用程序在屏幕上可见,但由于系统中断而不接收触摸事件
  • applicationWillResignActive — 指示 iOS 上进入 Inactive 的主要方法
  • 临时性质 — Inactive 持续时间为几分之一秒到几秒,然后进入 Active 或 Background
  • 原因 — 来电、Control Center、Notification Center、系统警报、Apple Pay、Siri
  • SwiftUI — scenePhase .inactive 等同于 UIKit 中的 Inactive

Inactive — 过渡状态的本质

Inactive — 移动应用程序生命周期的中间状态,发生在 Active 和 Background 之间切换时。在此状态下,应用程序仍然在前台运行并对用户可见,但不会收到触摸事件、按键事件和其他 UI 事件。系统阻止将事件传递给应用程序,但 UI 仍保留在屏幕上且不会最小化。

Inactive 的性质是临时性的。此状态持续的时间与系统中断持续的时间相同:从快速关闭 Control Center 时的 0.1 秒到收到来电并显示来电屏幕时的几秒钟。中断结束后,应用程序要么返回 Active,要么进入 Background(如果用户切换到另一个应用程序)。Inactive 是唯一可以双向转换的状态:返回 Active 或继续进入 Background。

在 iOS 上,Inactive 由系统自动管理。开发者无法延长或缩短 Inactive 的持续时间——这完全由 UIApplication 控制。开发者唯一能做的就是通过 applicationWillResignActive 正确处理进入 Inactive 以及通过 applicationDidBecomeActive 正确处理返回。在 Android 上,onPause 是类似的机制,但语义有所不同:即使 Activity 被其他组件部分覆盖,onPause 也会被调用。

iOS 和 Android 上的 Inactive 对比

在 iOS 上,Inactive 是应用程序生命周期的一个独立状态(共五个:Not Running、Active、Inactive、Background、Suspended)。在 Android 上没有直接的对应——onPause 表示 Activity 失去输入焦点,但仍可能可见(例如,打开对话框时)。关键区别:iOS Inactive 是整个应用程序的状态,而 Android onPause 是特定 Activity 的状态。在 Android 的多窗口模式下,一个 Activity 可能处于 onPause(无焦点),而另一个处于 onResume(有焦点)。

特性iOS InactiveAndroid onPause
UI 可见是(部分或全部)
触摸事件不接收不接收
持续时间直到中断结束直到焦点返回或进入后台
下一个状态Active 或 BackgroundonResume 或 onStop
级别应用程序(UIApplication)Activity
多窗口一个场景处于活动状态多个 Activity 处于 onPause

Inactive 何时发生

Inactive 在 iOS 上发生在几种严格定义的场景中。用户调出 Control Center(从 iPhone X+ 的右上角向下滑动,或在旧型号上向上滑动)。用户打开 Notification Center(从左上角向下滑动)。有来电——系统在应用程序上方显示来电屏幕。请求系统权限——地理位置、麦克风、相机、联系人。在 iPad 上启动 Slide Over 或 Split View——活动场景变为 Inactive。

在 Android 上,onPause(Inactive 的对应机制)发生在更广泛的场景中。打开对话框(AlertDialog、DialogFragment)。Activity 被另一个 Activity 部分覆盖(例如,用于认证的透明 Activity)。屏幕旋转(Activity 重新创建,顺序:onPause → onStop → onDestroy → onCreate → onStart → onResume)。多窗口模式——非活动窗口收到 onPause。这些事件中的每一个都需要暂停资源密集型操作以节省电池和提高性能。

swift
import UIKit

extension Notification.Name {
    static let systemInterruptionBegan = Notification.Name("systemInterruptionBegan")
    static let systemInterruptionEnded = Notification.Name("systemInterruptionEnded")
}

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func applicationWillResignActive(_ application: UIApplication) {
        // 应用程序进入 Inactive — 系统中断
        print("中断:Control Center、来电或系统警报")

        // 暂停时间敏感的操作
        pauseVideoPlayback()
        stopContinuousDataCollection()
        hideSensitiveInformation()

        // 通知组件
        NotificationCenter.default.post(name: .systemInterruptionBegan, object: nil)
    }

    // 从 Inactive 返回 Active
    func applicationDidBecomeActive(_ application: UIApplication) {
        resumeVideoPlayback()
        restartDataCollection()
        NotificationCenter.default.post(name: .systemInterruptionEnded, object: nil)
    }

    private func pauseVideoPlayback() {
        // 暂停视频以避免音频重叠
    }

    private func hideSensitiveInformation() {
        // 在屏幕截图时隐藏敏感数据
        // Control Center/App Switcher 截取 UI 屏幕截图
    }
}

代码展示了 UIKit 中 Inactive 的处理。applicationWillResignActive 暂停视频播放、停止数据收集并隐藏敏感信息。这很重要,因为在打开 Control Center 或 App Switcher 时,系统会截取当前 UI 的屏幕截图——用户可能会在预览中看到敏感数据。NotificationCenter 允许应用程序组件订阅中断事件。

iOS 中的 Inactive:Swift 和 UIKit

在 iOS 中,Inactive 由一对方法处理:applicationWillResignActive(进入 Inactive)和 applicationDidBecomeActive(从 Inactive 返回)。这些方法是 UIApplicationDelegate 的一部分,每次经过 Inactive 的转换都会被调用。从 iOS 13 和 UISceneDelegate 开始,增加了 sceneWillResignActive 和 sceneDidBecomeActive 用于多窗口场景。

SceneDelegate 和多窗口 Inactive

在运行 iOS 13+ 的 iPad 上,应用程序可以有多个场景(窗口)。每个场景都有自己的生命周期。一个场景可能变为 Inactive(用户切换到另一个场景),而另一个场景保持 Active。这是与 iPhone 的重要区别,在 iPhone 上 Inactive 是整个应用程序的全局状态。在开发 iPad 应用时,需要为每个场景单独处理 Inactive。

swift
import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    // 场景变为非活动状态
    func sceneWillResignActive(_ scene: UIScene) {
        // 在 iPad 上,此场景失去焦点,但其他场景可能仍处于活动状态
        print("场景失去活动状态")

        // 暂停此场景的任务
        pauseSceneSpecificOperations()
    }

    // 场景变为活动状态
    func sceneDidBecomeActive(_ scene: UIScene) {
        print("场景已变为活动状态")
        resumeSceneSpecificOperations()
    }

    private func pauseSceneSpecificOperations() {
        // 暂停特定于此场景的操作
    }

    private func resumeSceneSpecificOperations() {
        // 焦点返回时恢复操作
    }
}

// AppDelegate 仍然是入口点,委托给场景
@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(
        _ application: UIApplication,
        configurationForConnecting connectingSceneSession: UISceneSession,
        options: UIScene.ConnectionOptions
    ) -> UISceneConfiguration {
        return UISceneConfiguration(
            name: "Default Configuration",
            sessionRole: connectingSceneSession.role
        )
    }
}

代码展示了 SceneDelegate 在场景级别处理 Inactive 的方法。sceneWillResignActive 在特定窗口失去焦点时被调用——这可能在 iPad 上切换窗口时发生。AppDelegate 配置 UISceneConfiguration 以支持多窗口。每个场景都有独立的状态,开发者必须分别处理它们。

Android 中的 Inactive:onPause 和可见性

在 Android 中,iOS Inactive 的直接对应是 Activity 生命周期中的 onPause() 方法。当 Activity 失去输入焦点但仍可能可见时被调用。典型场景:打开对话框、在同一应用程序中启动另一个 Activity、来电、按下 Home 或 Recents 按钮。在 onPause 中,开发者应暂停资源密集型操作——动画、视频播放、相机操作。

Android 的一个重要区别是 onPause 总是在 onStop 之前,但反之不然。Activity 可能收到 onPause 但不收到 onStop(例如,打开透明 Activity 时)。此外,onPause 可能在 Activity 生命周期内被多次调用——每次焦点切换时。不要在 onPause 中放置一次性逻辑——使用 onStop 进行最终操作,onPause 仅用于暂停交互操作。

kotlin
class VideoPlayerActivity : AppCompatActivity() {

    private var exoPlayer: ExoPlayer? = null
    private var currentPosition: Long = 0L

    override fun onPause() {
        super.onPause()
        // 应用程序失去焦点 — 暂停视频播放
        exoPlayer?.let { player ->
            if (player.isPlaying) {
                currentPosition = player.currentPosition
                player.pause()
            }
        }
        // 隐藏敏感数据(GDPR/银行界面)
        if (window.decorView.systemUiVisibility and
            View.SYSTEM_UI_FLAG_SECURE == 0
        ) {
            hideSensitiveOverlay()
        }
    }

    override fun onResume() {
        super.onResume()
        // 焦点返回 — 恢复播放
        exoPlayer?.seekTo(currentPosition)
        exoPlayer?.play()
        showSensitiveOverlay()
    }

    private fun hideSensitiveOverlay() {
        // 在财务数据上叠加黑屏
    }
}

代码展示了视频播放器 onPause 的正确处理。ExoPlayer 在失去焦点时暂停,并保存播放位置。返回 onResume 时,播放器从保存的位置恢复播放。还展示了隐藏敏感数据的模式——这对于需要防止切换时截屏的金融和医疗应用程序非常重要。

处理 Inactive 的最佳实践

第一条规则——在进入 Inactive 时隐藏敏感数据。当用户打开 Control Center 或 App Switcher 时,iOS 会截取当前屏幕的截图。在 Android 上类似——系统在 Recents 中显示上一个 Activity 的预览。使用 UIApplication.shouldSnapshotSecureApp(iOS 16+)或 FLAG_SECURE(Android)来保护敏感界面。

第二条规则——暂停动画和媒体播放。Inactive 不是播放视频或动画的好时机,因为用户看不到它们。此外,后台播放可能会导致音频与系统声音(来电、通知)重叠。在进入 Inactive 时停止 AVPlayer、ExoPlayer 和 UIView.animate,并在返回 Active 时恢复。

第三条规则——阻止数据输入。如果应用程序包含输入表单或草稿,请在进入 Inactive 时锁定键盘和输入字段。这可以防止返回时的意外输入,并防止通过系统覆盖层拦截数据。在 iOS 上释放 first responder(view.endEditing(true)),在 Android 上清除焦点(currentFocus?.clearFocus())。

第四条规则——不要在 applicationWillResignActive 或 onPause 中执行长时间操作。这些方法应在几分之一秒内完成。如果需要保存大量数据,请在后台线程中开始保存,并在 applicationDidEnterBackground 或 onStop 中完成。iOS 给予 5 秒钟来执行 applicationWillResignActive,之后系统可能会强制终止应用程序。

swift
import UIKit

final class SecureOverlayManager {
    private var blurView: UIVisualEffectView?

    func showBlurOverlay() {
        guard let window = UIApplication.shared.keyWindow,
              blurView == nil
        else { return }

        let blur = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
        blur.frame = window.bounds
        blur.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        window.addSubview(blur)
        blurView = blur
    }

    func removeBlurOverlay() {
        blurView?.removeFromSuperview()
        blurView = nil
    }
}

// 在 AppDelegate 中的使用
func applicationWillResignActive(_ application: UIApplication) {
    SecureOverlayManager().showBlurOverlay()
}

func applicationDidBecomeActive(_ application: UIApplication) {
    SecureOverlayManager().removeBlurOverlay()
}

代码展示了在进入 Inactive 时保护数据的安全覆盖层实现。带有模糊效果的 UIVisualEffectView 在进入 Inactive 时覆盖在整个 UI 之上,并在返回 Active 时被移除。这确保了敏感数据不会出现在 App Switcher 和 Control Center 的屏幕截图中。类似地,可以使用带有标志的 UIImageView 作为品牌覆盖层。

常见问题

Inactive 是否总是在 Background 之前?

是的。Inactive 是进入 Background 之前的必需中间状态。应用程序不能直接从 Active 进入 Background——它首先变为 Inactive,然后进入 Background。在 Android 上类似:onPause 总是在 onStop 之前被调用。这使开发者有机会在完全进入后台之前准备要保存的数据。

应用程序可以在没有中断的情况下进入 Inactive 吗?

是的。在 iPad 上启动 Slide Over 或 Split View 时,活动场景变为 Inactive,虽然没有发生系统中断——用户只是与另一个场景交互。这是多窗口 iPadOS 的特性。在 iPhone 上,Inactive 总是由系统中断触发——来电、通知、Control Center 或 Notification Center。

应用程序可以在 Inactive 状态下停留多久?

通常为 0.1 到 2 秒。在有来电显示屏幕的情况下——最多 30 秒(直到用户接听或拒绝来电)。iOS 不会强制限制 Inactive 的时间,但如果应用程序对事件无响应,系统可能会终止它(watchdog)。在 Android 上,onPause 没有时间限制,但建议在 200 毫秒内完成。

SwiftUI 中 Inactive 时的 scenePhase 是什么?

ScenePhase.inactive——当场景在前台但不接收事件时设置的 ScenePhase 枚举值。在 SwiftUI 中,您可以通过 @Environment(\.scenePhase) 观察它,并通过 onChange 做出响应。当从 .active 转换为 .inactive 时,暂停定时器和动画。返回 .active 时恢复。进入 .background 时保存状态。

是否所有应用程序都需要在 Inactive 时隐藏 UI?

不,仅适用于处理敏感数据的应用程序:银行、医疗、企业、具有私密聊天功能的即时通讯应用。对于游戏和娱乐应用程序,不需要隐藏 UI。但是,在 Inactive 时暂停游戏和声音是一种良好实践,以避免音频与系统通知重叠。Apple 建议隐藏敏感数据,但并不要求这样做。

总结

  • Inactive — Active 和 Background 之间的过渡状态,应用程序可见但不可交互
  • 原因 — Control Center、Notification Center、来电、系统权限、Siri、Apple Pay
  • iOS 处理 — 使用 applicationWillResignActive 暂停动画、媒体和隐藏敏感数据
  • SwiftUI — 通过 Environment 使用 scenePhase .inactive,使用 onChange 执行副作用
  • Android — onPause 作为对应机制,支持多窗口和 Activity 部分覆盖
  • 安全性 — 模糊覆盖层或 FLAG_SECURE 以防止 App Switcher 和 Control Center 的屏幕截图
  • 临时性质 — Inactive 持续到中断结束,然后返回 Active 或进入 Background

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

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

讨论项目

另请阅读