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 — це базовий стан життєвого циклу мобільного додатку, в якому він не завантажений в оперативну пам’ять пристрою та не споживає системні ресурси. В iOS та Android цей стан означає повну відсутність процесів та потоків, пов’язаних з додатком. Користувач бачить іконку додатку на робочому столі, але сам додаток не активний та не знаходиться в списку нещодавніх.

Коли користувач натискає на іконку, система створює новий процес, завантажує виконуваний код у пам’ять та ініціалізує всі необхідні структури даних. Цей процес називається холодним стартом (cold start) і є найбільш ресурсоємним з точки зору часу завантаження.

Система може перемістити додаток у Not Running з будь-якого іншого стану. Якщо додаток знаходиться у фоновому режимі або призупинений (Suspended), операційна система має право вивантажити його при нестачі оперативної пам’яті для більш пріоритетних завдань — наприклад, для активного додатку на передньому плані.

Розробник повинен враховувати, що додаток може бути завершений системою в будь-який момент, коли він знаходиться у фоновому режимі. Це означає, що всі незбережені дані можуть бути втрачені. Тому критично важливо зберігати стан у key-value сховищах (UserDefaults, SharedPreferences) або в локальній базі даних при переходах з Active у Background.

Як система визначає, який додаток вивантажити

iOS використовує пріоритети на основі поточного стану додатку: Active має найвищий пріоритет, потім Inactive, Background, Suspended і, нарешті, Not Running — найнижчий пріоритет. Android використовує подібну ієрархію процесів: процес переднього плану має пріоритет OOM_ADJ = 0, Visible процес = 100, Service процес = 200, Background процес = 300, Empty процес = 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 — не більше 400 мс для iOS.

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-делегат, щоб не блокувати перший кадр. onPostCreate — оптимальне місце для ініціалізації важких модулів, оскільки UI вже відмальований.

Not Running у iOS: Swift та AppDelegate

У iOS Not Running керується через делегат UIApplicationDelegate. Ключові методи: application(_:didFinishLaunchingWithOptions:) викликається після холодного старту, applicationWillTerminate(_:) викликається перед завершенням додатку користувачем. При цьому система може завершити додаток без виклику applicationWillTerminate — наприклад, при аварійному завершенні або вивантаженні пам’яті. iOS не гарантує виклик цього методу, тому зберігати дані потрібно в applicationDidEnterBackground.

Сценарії переходу в Not Running на iOS

Користувач може вручну завершити додаток свайпом в App Switcher. Система може вивантажити додаток з пам’яті у фоновому режимі. Додаток може аварійно завершитися (crash). У всіх випадках всі об’єкти, створені при запуску, знищуються. Стан, який не був збережений, втрачається безповоротно. У iOS 13+ для збереження стану рекомендується використовувати NSUserActivity або механізм state restoration через 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
        )
    }
}

У коді показано коректну обробку Not Running на iOS. applicationWillTerminate викликається тільки при ручному завершенні користувачем. Збереження критичних даних дублюється в applicationDidEnterBackground, оскільки цей метод гарантовано викликається перед переходом у фон. State restoration дозволяє зберегти UI-стек для подальшого відновлення при холодному старті.

Not Running у Android: Kotlin та процес

В Android Not Running означає, що процес додатку не існує. Система Linux, на якій базується Android, керує процесами через механізм Zygote. При запуску додатку Zygote форкає новий процес, завантажує Dalvik/ART та викликає Application.onCreate(). В Android немає прямого аналога applicationWillTerminate — система може завершити процес у будь-який момент без попередження.

Життєвий цикл процесу Android

Коли Activity викликається вперше, система створює процес, Application та Activity через ланцюжок onCreate → onStart → onResume. Якщо користувач натискає Назад, Activity знищується (onDestroy), і процес може бути завершений системою. Ключова відмінність від iOS: в Android процес може продовжувати існувати навіть без активних Activity — наприклад, якщо працює Foreground Service або є активний BroadcastReceiver.

kotlin
// Обробка 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

Not Running настає з кількох причин. Користувач вручну закриває додаток. Система вивантажує додаток при нестачі пам’яті. Додаток аварійно завершується з винятком. На Android система може завершити процес при масовому оновленні додатків або перезавантаженні пристрою. iOS може завершити додаток при вичерпанні таймауту фонового завдання (зазвичай 30 секунд).

ПричинаiOSAndroidМожливість запобігти
Ручне закриття користувачемСвайп в App SwitcherСвайп з RecentsНі — дія користувача
Нестача пам’ятіСпрацювання memory warningonTrimMemory / LMKЧастково — оптимізація пам’яті
Краш додаткуNSException / сигналUncaughtException / ANRТак — обробка помилок та краш-репортинг
Таймаут фонового завдання30 сек на Background task10 хв на JobSchedulerТак — правильне планування завдань
Перезавантаження ОСВиклик applicationWillTerminateBroadcast ACTION_SHUTDOWNНі — системна подія
Оновлення додаткуНе відбувається (iOS Sandbox)Процес завершується при APK-оновленніНі — системне оновлення

Як діагностувати перехід в Not Running

Для iOS використовуйте консольне логування в applicationWillTerminate та applicationDidFinishLaunching. Додайте прапорець в UserDefaults при кожному запуску — якщо при наступному старті прапорець відсутній, додаток був завершений некоректно. На Android використовуйте ActivityManager.isBackgroundRestricted(), щоб перевірити, чи може додаток запускати фонові завдання. Також відстежуйте onTrimMemory(TRIM_MEMORY_COMPLETE) — це сигнал, що процес буде завершений.

Найкращі практики роботи з Not Running

Перше правило — ніколи не розраховуйте, що 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-сповіщення, універсальні посилання — всі вони передаються через параметри запуску. Розробник повинен коректно витягти ці дані та направити користувача на відповідний екран.

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 links та універсальні посилання передаються через цей словник. Розробник повинен коректно обробити всі можливі сценарії запуску, щоб забезпечити безшовний користувацький досвід.

Часто задавані питання

Що відбувається з даними при переході в Not Running?

Дані, які були збережені в постійне сховище (UserDefaults, Core Data, SharedPreferences, Room), зберігаються. Дані в оперативній пам’яті — змінні, кеш, стан ViewModel без SavedStateHandle — втрачаються безповоротно. Тому критично важливо зберігати стан додатку при кожному переході в Background.

Як відрізнити холодний старт від гарячого на iOS?

При холодному старті викликається application(_:didFinishLaunchingWithOptions:). При гарячому старті (повернення з Suspended) цей метод не викликається — спрацьовують тільки applicationWillEnterForeground та applicationDidBecomeActive. Якщо вам потрібно виконати дію тільки при холодному старті, встановіть прапорець в didFinishLaunchingWithOptions.

Чи може Android додаток бути в Not Running з активним Service?

Так. Foreground Service з постійним сповіщенням запобігає завершенню процесу системою, навіть якщо всі Activity знищені. Background Service (startService без foreground) може бути зупинений системою в будь-який час. Працюючий Service означає, що процес існує, і це вже не Not Running.

Як емулювати Not Running на симуляторі?

На iOS симуляторі завершіть додаток через App Switcher (Cmd+Shift+H двічі, свайп вгору). На Android емуляторі використовуйте adb shell am force-stop com.example.app або кнопку Stop в Logcat. Після цього запустіть додаток знову — це буде чистий холодний старт з Not Running.

Що таке kill-switch в контексті Not Running?

Kill-switch — серверна команда екстреного завершення додатку. Використовується в банківських та корпоративних додатках для віддаленого блокування доступу. Якщо додаток отримав kill-команду, при наступному холодному старті він блокує UI та запитує повторну авторизацію. На iOS kill-switch реалізується через remote notifications з прапорцем блокування.

Підсумки

  • Not Running — початковий та кінцевий стан життєвого циклу, додаток не завантажений в пам’ять та не виконує код
  • Холодний старт — повне перезавантаження додатку з Not Running, потребує ініціалізації всіх компонентів з нуля
  • Гарячий старт — повернення з Suspended, не викликає didFinishLaunchingWithOptions або Application.onCreate
  • Збереження даних — критично важливо виконувати при переході в Background, оскільки Not Running може настати в будь-який момент
  • iOS — applicationWillTerminate не гарантований, стан зберігається через UserDefaults або state restoration
  • Android — процес може бути завершений в будь-який момент, SavedStateHandle в ViewModel зберігає стан автоматично
  • Оптимізація старту — лінива ініціалізація, мінімальна робота в main thread, SplashScreen API для швидкого першого кадру

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також