Not Running — начальное состояние жизненного цикла мобильного приложения, в котором оно ещё не запущено или уже завершило работу. Узнайте, как система iOS и Android управляют этим состоянием, какие события приводят к переходу из Not Running и как правильно обрабатывать запуск и завершение приложения в Swift и Kotlin.
Главное
Not Running — это базовое состояние жизненного цикла мобильного приложения, в котором оно не загружено в оперативную память устройства и не потребляет системные ресурсы. В iOS и Android это состояние означает полное отсутствие процессов и потоков, связанных с приложением. Пользователь видит иконку приложения на рабочем столе, но само приложение не активно и не находится в списке недавних.
Когда пользователь нажимает на иконку, система создаёт новый процесс, загружает исполняемый код в память и инициализирует все необходимые структуры данных. Этот процесс называется холодным стартом (cold start) и является самым ресурсоёмким с точки зрения времени загрузки.
Система может переместить приложение в Not Running из любого другого состояния. Если приложение находится в фоне (Background) или приостановлено (Suspended), операционная система имеет право выгрузить его при нехватке оперативной памяти для более приоритетных задач — например, для активного приложения на переднем плане.
Разработчик должен учитывать, что приложение может быть завершено системой в любой момент, когда оно находится в фоне. Это означает, что все несохранённые данные могут быть потеряны. Поэтому критически важно сохранять состояние в key-value хранилища (UserDefaults, SharedPreferences) или в локальную базу данных на переходах из Active в Background.
iOS использует приоритеты на основе текущего состояния приложения: Active имеет наивысший приоритет, затем Inactive, Background, Suspended и, наконец, Not Running — минимальный приоритет. Android использует похожую иерархию процессов: Foreground процесс имеет приоритет OOM_ADJ = 0, Visible процесс = 100, Service процесс = 200, Background процесс = 300, Empty процесс = 400. Чем выше значение, тем больше вероятность, что процесс будет завершён при нехватке памяти.
| Платформа | Состояние | Приоритет выгрузки | Описание |
|---|---|---|---|
| iOS | Not Running | Наивысший | Приложение не загружено — системный ресурс не потребляется |
| iOS | Suspended | Высокий | Приложение в памяти, но код не выполняется — первая цель для выгрузки |
| iOS | Background | Средний | Приложение выполняет фоновую задачу — выгружается после таймаута |
| iOS | Active | Низкий | Активное приложение — выгружается только при критической нехватке памяти |
| Android | Empty Process | Наивысший | Процесс без активных компонентов — удаляется первым |
| Android | Background Process | Высокий | Фоновый процесс без видимого Activity |
| Android | Foreground Service | Низкий | Сервис с уведомлением — редко завершается |
| Android | Foreground 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 — не более 400 мс для iOS.
// Измерение времени холодного старта в 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-делегат, чтобы не блокировать первый кадр. onPostCreate — оптимальное место для инициализации тяжёлых модулей, так как UI уже отрисован.
В iOS Not Running управляется через делегат UIApplicationDelegate. Ключевые методы: application(_:didFinishLaunchingWithOptions:) вызывается после холодного старта, applicationWillTerminate(_:) вызывается перед завершением приложения пользователем. При этом система может завершить приложение без вызова applicationWillTerminate — например, при аварийном завершении или выгрузке памяти. iOS не гарантирует вызов этого метода, поэтому сохранять данные нужно в applicationDidEnterBackground.
Пользователь может вручную завершить приложение свайпом в App Switcher. Система может выгрузить приложение из памяти в фоне. Приложение может аварийно завершиться (crash). Во всех случаях все объекты, созданные при запуске, уничтожаются. Состояние, которое не было сохранено, теряется безвозвратно. В iOS 13+ для сохранения состояния рекомендуется использовать NSUserActivity или механизм state restoration через UIApplication.stateRestorationIdentifier.
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
)
}
}В коде показана корректная обработка Not Running на iOS. applicationWillTerminate вызывается только при ручном завершении пользователем. Сохранение критических данных дублируется в applicationDidEnterBackground, так как этот метод гарантированно вызывается перед уходом в фон. State restoration позволяет сохранить UI-стек для последующего восстановления при холодном старте.
В Android Not Running означает, что процесс приложения не существует. Система Linux, на которой основан Android, управляет процессами через механизм Zygote. При запуске приложения Zygote форкает новый процесс, загружает Dalvik/ART и вызывает Application.onCreate(). В Android нет прямого аналога applicationWillTerminate — система может завершить процесс в любой момент без предупреждения.
Когда Activity вызывается впервые, система создаёт процесс, Application и Activity через цепочку onCreate → onStart → onResume. Если пользователь нажимает Back, Activity уничтожается (onDestroy), и процесс может быть завершён системой. Ключевое отличие от iOS: в Android процесс может продолжать существовать даже без активных Activity — например, если работает Foreground Service или есть активный BroadcastReceiver.
// Обработка Not Running через SavedStateHandle в ViewModel
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 и восстанавливает его при холодном старте. ViewModel, созданная через ViewModelProvider, переживает поворот экрана и уничтожение Activity. При завершении процесса данные из SavedStateHandle сериализуются в Bundle и сохраняются в saved instance state.
Not Running наступает по нескольким причинам. Пользователь вручную закрывает приложение. Система выгружает приложение при нехватке памяти. Приложение аварийно завершается с исключением. На Android система может завершить процесс при массовом обновлении приложений или перезагрузке устройства. iOS может завершить приложение при истечении таймаута фоновой задачи (обычно 30 секунд).
| Причина | iOS | Android | Возможность предотвратить |
|---|---|---|---|
| Ручное закрытие пользователем | Свайп в App Switcher | Свайп из Recents | Нет — пользовательское действие |
| Нехватка памяти | Срабатывание memory warning | onTrimMemory / LMK | Частично — оптимизация памяти |
| Краш приложения | NSException / сигнал | UncaughtException / ANR | Да — обработка ошибок и краш-репортинг |
| Таймаут фоновой задачи | 30 сек на Background task | 10 мин на JobScheduler | Да — правильное планирование задач |
| Перезагрузка ОС | Вызов applicationWillTerminate | Broadcast ACTION_SHUTDOWN | Нет — системное событие |
| Обновление приложения | Не происходит (iOS Sandbox) | Процесс завершается при APK-обновлении | Нет — системное обновление |
Для iOS используйте консольное логирование в applicationWillTerminate и applicationDidFinishLaunching. Добавьте флаг в UserDefaults при каждом запуске — если при следующем старте флага нет, приложение было завершено некорректно. На Android используйте ActivityManager.isBackgroundRestricted(), чтобы проверить, может ли приложение запускать фоновые задачи. Также отслеживайте onTrimMemory(TRIM_MEMORY_COMPLETE) — это сигнал, что процесс будет завершён.
Первое правило — никогда не рассчитывайте, что applicationWillTerminate или onDestroy будут вызваны. Сохраняйте критически важные данные при каждом переходе из Active в Background. Используйте key-value хранилища для простых настроек и SQLite/Room для структурированных данных.
Второе правило — измеряйте время холодного старта и оптимизируйте его. Ленивая инициализация, минимизация работы в главном потоке, предварительная загрузка ресурсов, использование SplashScreen API — всё это улучшает восприятие времени запуска. Google рекомендует холодный старт менее 200 мс для отличного UX.
Третье правило — реализуйте State Restoration. На iOS используйте UIApplication.stateRestorationIdentifier и NSUserActivity. На Android используйте SavedStateHandle в ViewModel в сочетании с onSaveInstanceState. Это позволит пользователю продолжить работу с того же места после перезапуска приложения.
Четвёртое правило — обрабатывайте launchOptions и Intent, с которыми приложение было запущено после Not Running. Deep links, push-уведомления, универсальные ссылки — все они передаются через параметры запуска. Разработчик должен корректно извлечь эти данные и направить пользователя на соответствующий экран.
// Обработка 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 links и универсальные ссылки передаются через этот словарь. Разработчик должен корректно обработать все возможные сценарии запуска, чтобы обеспечить бесшовный пользовательский опыт.
Часто задаваемые вопросы
Данные, которые были сохранены в постоянное хранилище (UserDefaults, Core Data, SharedPreferences, Room), сохраняются. Данные в оперативной памяти — переменные, кеш, состояние ViewModel без SavedStateHandle — теряются безвозвратно. Поэтому критически важно сохранять состояние приложения при каждом переходе в Background.
При холодном старте вызывается application(_:didFinishLaunchingWithOptions:). При горячем старте (возврат из Suspended) этот метод не вызывается — срабатывают только applicationWillEnterForeground и applicationDidBecomeActive. Если вам нужно выполнить действие только при холодном старте, установите флаг в didFinishLaunchingWithOptions.
Да. Foreground Service с постоянным уведомлением предотвращает завершение процесса системой, даже если все Activity уничтожены. Background Service (startService без foreground) может быть остановлен системой в любое время. Работающий Service означает, что процесс существует, и это уже не Not Running.
На iOS симуляторе завершите приложение через App Switcher (Cmd+Shift+H дважды, свайп вверх). На Android эмуляторе используйте adb shell am force-stop com.example.app или кнопку Stop в Logcat. После этого запустите приложение заново — это будет чистый холодный старт из Not Running.
Kill-switch — серверная команда экстренного завершения приложения. Используется в банковских и корпоративных приложениях для удалённой блокировки доступа. Если приложение получило kill-команду, при следующем холодном старте оно блокирует UI и запрашивает повторную авторизацию. На iOS kill-switch реализуется через remote notifications с флагом блокировки.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также