Not Running — 什么是它,生命周期的初始状态

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

Not Running — 移动应用程序生命周期的初始状态,应用程序尚未启动或已完成运行。了解 iOS 和 Android 系统如何管理此状态,哪些事件导致从 Not Running 转换,以及如何在 Swift 和 Kotlin 中正确处理应用程序的启动和终止。

要点

  • Not Running — 应用程序未加载到内存且不执行代码,这是生命周期中的入口和出口点
  • 启动 — 从 Not Running 转换发生在点击图标时,通过 deep link 或 push 通知
  • 终止 — 用户通过滑动关闭应用程序,系统在内存不足时卸载它或发生崩溃
  • 冷启动 — 应用程序从零开始,所有对象重新创建,状态不从缓存恢复
  • 热启动 — 应用程序处于 Suspended 状态,无需完全初始化即可返回 Active

Not Running — 这是什么状态

Not Running — 是移动应用程序生命周期的基本状态,应用程序未加载到设备的 RAM 中且不消耗系统资源。在 iOS 和 Android 中,此状态意味着与应用程序相关的进程和线程完全不存在。用户在桌面上看到应用程序图标,但应用程序本身不活动且不在最近列表中。

当用户点击图标时,系统创建一个新进程,将可执行代码加载到内存中并初始化所有必要的数据结构。此过程称为冷启动(cold start),是加载时间方面最消耗资源的过程。

系统可以将应用程序从任何其他状态移动到 Not Running。如果应用程序在后台(Background)或挂起状态(Suspended),操作系统有权在 RAM 不足时卸载它以执行更优先的任务——例如,用于前台的活动应用程序。

开发人员必须注意,应用程序在后台时可能随时被系统终止,这意味着所有未保存的数据都可能会丢失。因此,在从 Active 转换到 Background 时,将状态保存到键值存储(UserDefaults、SharedPreferences)或本地数据库至关重要。

系统如何确定卸载哪个应用程序

iOS 根据应用程序的当前状态使用优先级:Active 具有最高优先级,然后是 Inactive、Background、Suspended,最后是 Not Running — 最低优先级。Android 使用类似的进程层次结构:前台进程的优先级为 OOM_ADJ = 0,可见进程 = 100,服务进程 = 200,后台进程 = 300,空进程 = 400。值越高,进程在内存不足时被终止的可能性越大。

平台状态卸载优先级描述
iOSNot Running最高应用程序未加载 — 不消耗系统资源
iOSSuspended应用程序在内存中,但代码不执行 — 卸载的首要目标
iOSBackground应用程序执行后台任务 — 超时后卸载
iOSActive活动应用程序 — 仅在内存严重不足时卸载
AndroidEmpty Process最高没有活跃组件的进程 — 首先被删除
AndroidBackground Process没有可见 Activity 的后台进程
AndroidForeground Service带有通知的服务 — 很少被终止
AndroidForeground Process最低活跃的 Activity — 最后被终止

应用程序的冷启动和热启动

冷启动(cold start)发生在应用程序从 Not Running 直接转换到 Active 时。系统创建一个新进程,加载类,初始化静态字段,创建主线程并启动 UI 框架。在 iOS 中这意味着调用 application(_:didFinishLaunchingWithOptions:),在 Android 中——调用 Application.onCreate() 和 Activity.onCreate()。冷启动时间根据应用程序的复杂程度可以从 200 毫秒到几秒不等。

热启动(warm start 或 hot start)——应用程序处于 Suspended 状态,无需完全重新加载即可恢复运行。系统从内存中恢复最后的 UI 堆栈,用户从同一位置继续工作。热启动明显快于冷启动,因为大部分代码已加载到内存中。在 iOS 中,热启动不调用 application(_:didFinishLaunchingWithOptions:),只调用 applicationWillEnterForeground 和 applicationDidBecomeActive。

冷启动和热启动之间的区别对用户体验至关重要。在冷启动时,开发人员必须确保启动尽可能快——模块的惰性初始化、延迟加载重型资源、启动时最小化主线程的工作。Google 建议冷启动不超过 500 毫秒,Apple 建议 iOS 不超过 400 毫秒。

kotlin
// 测量 Android 中的冷启动时间
class App : Application() {
    private var startTime: Long = 0L

    override fun onCreate() {
        super.onCreate()
        startTime = System.currentTimeMillis()
    }

    fun getStartupTime(): Long {
        return System.currentTimeMillis() - startTime
    }
}

// 使用惰性初始化启动 Activity
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by lazy {
        ViewModelProvider(this).get(MainViewModel::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 仅第一帧所需的最小值
        setupNavigation()
    }

    override fun onPostCreate(savedInstanceState: Bundle?) {
        super.onPostCreate(savedInstanceState)
        // 渲染后的重型初始化
        initializeHeavyModules()
    }
}

示例展示了 Android 中冷启动时间的测量。Application.onCreate() 在从 Not Running 到 Active 的转换时调用。时间戳在进程启动时记录。Activity 通过 lazy-delegate 使用惰性初始化,以避免阻塞第一帧。onPostCreate 是初始化重型模块的最佳位置,因为 UI 已经渲染。

iOS 中的 Not Running:Swift 和 AppDelegate

在 iOS 中,Not Running 通过 UIApplicationDelegate 委托管理。关键方法:application(_:didFinishLaunchingWithOptions:) 在冷启动后调用,applicationWillTerminate(_:) 在用户终止应用程序前调用。系统可以在不调用 applicationWillTerminate 的情况下终止应用程序——例如,在紧急终止或内存卸载时。iOS 不保证调用此方法,因此数据应在 applicationDidEnterBackground 中保存。

iOS 中进入 Not Running 的场景

用户可以通过在 App Switcher 中滑动手动终止应用程序。系统可以在后台从内存中卸载应用程序。应用程序可能因崩溃而紧急终止。在所有情况下,启动时创建的所有对象都被销毁。未保存的状态将永久丢失。在 iOS 13+ 中,建议使用 NSUserActivity 或通过 UIApplication.stateRestorationIdentifier 的状态恢复机制来保存状态。

swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    // 冷启动:应用程序从 Not Running 转换
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // 最小服务集的初始化
        setupAnalytics()
        configureAppearance()
        return true
    }

    // 应用程序结束运行 — 仅手动关闭
    func applicationWillTerminate(
        _ application: UIApplication
    ) {
        saveCriticalData()
    }

    // 进入后台前保存数据
    func applicationDidEnterBackground(
        _ application: UIApplication
    ) {
        saveApplicationState()
    }

    private func saveCriticalData() {
        UserDefaults.standard.synchronize()
    }

    private func saveApplicationState() {
        let state = ["lastScreen": "main", "timestamp": Date()]
        try? NSKeyedArchiver.archivedData(
            withRootObject: state,
            requiringSecureCoding: true
        )
    }
}

代码展示了 iOS 中 Not Running 的正确处理。applicationWillTerminate 仅在用户手动终止时调用。关键数据的保存在 applicationDidEnterBackground 中重复,因为此方法保证在进入后台前调用。状态恢复允许保存 UI 堆栈,以便在冷启动时后续恢复。

Android 中的 Not Running:Kotlin 和进程

在 Android 中,Not Running 意味着应用程序的进程不存在。Android 所基于的 Linux 系统通过 Zygote 机制管理进程。启动应用程序时,Zygote 创建一个新进程,加载 Dalvik/ART 并调用 Application.onCreate()。Android 中没有 applicationWillTerminate 的直接类似物——系统可以随时终止进程而不发出警告。

Android 进程的生命周期

当 Activity 第一次被调用时,系统通过链 onCreate → onStart → onResume 创建进程、Application 和 Activity。如果用户按下 Back,Activity 被销毁(onDestroy),进程可能被系统终止。与 iOS 的主要区别:在 Android 中,即使没有活跃的 Activity,进程也可以继续存在——例如,如果前台服务正在运行或有活跃的 BroadcastReceiver。

kotlin
// 通过 ViewModel 中的 SavedStateHandle 处理 Not Running
class MainViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    companion object {
        private const val KEY_LAST_SCREEN = "last_screen"
        private const val KEY_USER_DATA = "user_data"
    }

    fun saveCurrentState(screen: String, data: String) {
        savedStateHandle[KEY_LAST_SCREEN] = screen
        savedStateHandle[KEY_USER_DATA] = data
    }

    fun restoreState(): AppState? {
        val screen = savedStateHandle.get<String>(KEY_LAST_SCREEN)
        val data = savedStateHandle.get<String>(KEY_USER_DATA)
        return if (screen != null && data != null) {
            AppState(screen, data)
        } else null
    }
}

// Application — Not Running 后的第一个回调
class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        initCrashReporter()
        initDependencyInjection()
    }
}

SavedStateHandle——Android Architecture Components 的一个组件,在转换到 Not Running 时自动保存状态并在冷启动时恢复它。通过 ViewModelProvider 创建的 ViewModel 能够承受屏幕旋转和 Activity 销毁。进程终止时,来自 SavedStateHandle 的数据序列化为 Bundle 并保存在 saved instance state 中。

进入 Not Running 的原因

Not Running 由几个原因引起。用户手动关闭应用程序。系统在内存不足时卸载应用程序。应用程序因异常紧急终止。在 Android 中,系统可以在应用程序批量更新或设备重启时终止进程。iOS 可以在后台任务超时(通常为 30 秒)时终止应用程序。

原因iOSAndroid预防可能性
用户手动关闭在 App Switcher 中滑动从 Recents 滑动否 — 用户操作
内存不足触发 memory warningonTrimMemory / LMK部分 — 内存优化
应用程序崩溃NSException / 信号UncaughtException / ANR是 — 错误处理和崩溃报告
后台任务超时后台任务 30 秒JobScheduler 10 分钟是 — 正确的任务规划
操作系统重启调用 applicationWillTerminate广播 ACTION_SHUTDOWN否 — 系统事件
应用程序更新不发生(iOS Sandbox)APK 更新时进程终止否 — 系统更新

如何诊断进入 Not Running

对于 iOS,在 applicationWillTerminate 和 applicationDidFinishLaunching 中使用控制台日志记录。每次启动时向 UserDefaults 添加标志——如果下次启动时标志不存在,应用程序未正确终止。在 Android 中使用 ActivityManager.isBackgroundRestricted() 检查应用程序是否可以运行后台任务。同时监控 onTrimMemory(TRIM_MEMORY_COMPLETE)——这是进程将被终止的信号。

使用 Not Running 的最佳实践

第一条规则——永远不要假设会调用 applicationWillTerminate 或 onDestroy。在每次从 Active 转换到 Background 时保存关键重要数据。使用键值存储来保存简单设置,使用 SQLite/Room 来保存结构化数据。

第二条规则——测量冷启动时间并优化它。惰性初始化、最小化主线程中的工作、预加载资源、使用 SplashScreen API——所有这些都能改善启动时间的感知。Google 建议冷启动低于 200 毫秒以获得出色的用户体验。

第三条规则——实现状态恢复(State Restoration)。在 iOS 中使用 UIApplication.stateRestorationIdentifier 和 NSUserActivity。在 Android 中使用 ViewModel 中的 SavedStateHandle 结合 onSaveInstanceState。这将允许用户在应用程序重启后从同一位置继续工作。

第四条规则——处理应用程序在 Not Running 后启动的 launchOptions 和 Intent。Deep link、push 通知、通用链接——所有这些都通过启动参数传递。开发人员必须正确提取这些数据并将用户引导到相应的屏幕。

swift
// 冷启动后处理 deep link
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    // 检查是否收到通知
    if let notification = launchOptions?[.remoteNotification] as? [String: Any] {
        handleNotification(notification)
    }
    // 检查 deep link
    if let url = launchOptions?[.url] as? URL {
        handleDeepLink(url)
    }
    return true
}

private func handleDeepLink(_ url: URL) {
    guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
          let screenId = components.queryItems?.first(where: { $0.name == "screen" })?.value
    else { return }
    openScreen(screenId)
}

代码展示了 iOS 冷启动时启动参数的处理。launchOptions 包含系统启动应用程序所用的数据。通知、deep link 和通用链接通过此字典传递。开发人员必须正确处理所有可能的启动场景,以确保无缝的用户体验。

常见问题

转换到 Not Running 时数据会怎样?

保存在永久存储(UserDefaults、Core Data、SharedPreferences、Room)中的数据得以保留。RAM 中的数据——变量、缓存、没有 SavedStateHandle 的 ViewModel 状态——将永久丢失。因此,每次转换到后台时保存应用程序状态至关重要。

如何在 iOS 上区分冷启动和热启动?

冷启动时会调用 application(_:didFinishLaunchingWithOptions:)。热启动时(从 Suspended 返回)不调用此方法——只激活 applicationWillEnterForeground 和 applicationDidBecomeActive。如果需要在冷启动时执行操作,请在 didFinishLaunchingWithOptions 中设置标志。

Android 应用程序能否在 Not Running 状态下有活跃的 Service?

能。带有永久通知的 Foreground Service 可防止系统终止进程,即使所有 Activity 都被销毁。Background Service(不带前台启动的 startService)可能随时被系统停止。正在运行的 Service 意味着进程存在,这不再是 Not Running。

如何在模拟器上模拟 Not Running?

在 iOS 模拟器上,通过 App Switcher 终止应用程序(Cmd+Shift+H 两次,向上滑动)。在 Android 模拟器上,使用 adb shell am force-stop com.example.app 或 Logcat 中的 Stop 按钮。然后重新启动应用程序——这将是从 Not Running 的干净冷启动。

在 Not Running 上下文中什么是 kill-switch?

Kill-switch——用于紧急终止应用程序的服务器命令。用于银行和企业应用程序中远程阻止访问。如果应用程序收到 kill 命令,在下一次冷启动时会阻止 UI 并要求重新授权。在 iOS 中,kill-switch 通过带有阻止标志的远程通知实现。

总结

  • Not Running——生命周期的初始和最终状态,应用程序未加载到内存且不执行代码
  • 冷启动——从 Not Running 完全重新加载应用程序,需要从零初始化所有组件
  • 热启动——从 Suspended 返回,不调用 didFinishLaunchingWithOptions 或 Application.onCreate
  • 数据保存——转换到 Background 时至关重要,因为 Not Running 可能随时发生
  • iOS——applicationWillTerminate 不保证,状态通过 UserDefaults 或状态恢复保存
  • Android——进程可以随时终止,ViewModel 中的 SavedStateHandle 自动保存状态
  • 启动优化——惰性初始化,主线程中的最小工作,SplashScreen API 实现快速第一帧

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

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

讨论项目

另请阅读