Not Running — co to jest, początkowy stan cyklu życia

Autor: IT Sectr Opublikowano: 2026-03-03 Czas czytania: 11 min

Not Running — początkowy stan cyklu życia aplikacji mobilnej, w którym nie została jeszcze uruchomiona lub już zakończyła działanie. Dowiedz się, jak system iOS i Android zarządzają tym stanem, jakie zdarzenia prowadzą do przejścia z Not Running i jak prawidłowo obsługiwać uruchamianie i zakończenie aplikacji w Swift i Kotlin.

Najważniejsze

  • Not Running — aplikacja nie jest załadowana do pamięci i nie wykonuje kodu, to punkt wejścia i wyjścia w cyklu życia
  • Uruchomienie — przejście z Not Running następuje po dotknięciu ikony, przez deep link lub powiadomienie push
  • Zakończenie — użytkownik zamyka aplikację przesunięciem, system zwalnia ją przy braku pamięci lub następuje crash
  • Zimny start — aplikacja uruchamia się od zera, wszystkie obiekty są tworzone od nowa, stan nie jest przywracany z pamięci podręcznej
  • Gorący start — aplikacja była w Suspended i wraca do Active bez pełnej inicjalizacji

Not Running — co to za stan

Not Running — to podstawowy stan cyklu życia aplikacji mobilnej, w którym nie jest załadowana do pamięci RAM urządzenia i nie zużywa zasobów systemowych. W iOS i Android ten stan oznacza całkowity brak procesów i wątków związanych z aplikacją. Użytkownik widzi ikonę aplikacji na pulpicie, ale sama aplikacja nie jest aktywna i nie znajduje się na liście ostatnich.

Gdy użytkownik dotyka ikony, system tworzy nowy proces, ładuje kod wykonywalny do pamięci i inicjalizuje wszystkie niezbędne struktury danych. Ten proces nazywa się zimnym startem (cold start) i jest najbardziej zasobożerny pod względem czasu ładowania.

System może przenieść aplikację do Not Running z dowolnego innego stanu. Jeśli aplikacja znajduje się w tle (Background) lub jest zawieszona (Suspended), system operacyjny ma prawo zwolnić ją przy braku pamięci RAM dla bardziej priorytetowych zadań — na przykład dla aktywnej aplikacji na pierwszym planie.

Deweloper musi pamiętać, że aplikacja może zostać zakończona przez system w dowolnym momencie, gdy znajduje się w tle. Oznacza to, że wszystkie niezapisane dane mogą zostać utracone. Dlatego krytycznie ważne jest zapisywanie stanu w magazynach klucz-wartość (UserDefaults, SharedPreferences) lub w lokalnej bazie danych przy przejściach z Active do Background.

Jak system określa, którą aplikację zwolnić

iOS używa priorytetów na podstawie bieżącego stanu aplikacji: Active ma najwyższy priorytet, następnie Inactive, Background, Suspended i wreszcie Not Running — minimalny priorytet. Android używa podobnej hierarchii procesów: proces Foreground ma priorytet OOM_ADJ = 0, proces Visible = 100, proces Service = 200, proces Background = 300, proces Empty = 400. Im wyższa wartość, tym większe prawdopodobieństwo, że proces zostanie zakończony przy braku pamięci.

PlatformaStanPriorytet zwolnieniaOpis
iOSNot RunningNajwyższyAplikacja nie jest załadowana — zasób systemowy nie jest zużywany
iOSSuspendedWysokiAplikacja w pamięci, ale kod nie jest wykonywany — pierwszy cel do zwolnienia
iOSBackgroundŚredniAplikacja wykonuje zadanie w tle — zwalniana po przekroczeniu limitu czasu
iOSActiveNiskiAktywna aplikacja — zwalniana tylko przy krytycznym braku pamięci
AndroidEmpty ProcessNajwyższyProces bez aktywnych komponentów — usuwany jako pierwszy
AndroidBackground ProcessWysokiProces w tle bez widocznego Activity
AndroidForeground ServiceNiskiUsługa z powiadomieniem — rzadko kończona
AndroidForeground ProcessMinimalnyAktywne Activity — kończone jako ostatnie

Zimny i gorący start aplikacji

Zimny start (cold start) występuje, gdy aplikacja przechodzi z Not Running bezpośrednio do Active. System tworzy nowy proces, ładuje klasy, inicjalizuje pola statyczne, tworzy główny wątek i uruchamia framework UI. W iOS oznacza to wywołanie application(_:didFinishLaunchingWithOptions:), w Android — wywołanie Application.onCreate() i Activity.onCreate(). Czas zimnego startu może wynosić od 200 ms do kilku sekund w zależności od złożoności aplikacji.

Gorący start (warm start lub hot start) — aplikacja była w stanie Suspended i wraca do działania bez pełnego przeładowania. System przywraca ostatni stos UI z pamięci, a użytkownik kontynuuje pracę od tego samego miejsca. Gorący start jest znacznie szybszy niż zimny, ponieważ większość kodu jest już załadowana do pamięci. W iOS gorący start nie wywołuje application(_:didFinishLaunchingWithOptions:), tylko applicationWillEnterForeground i applicationDidBecomeActive.

Różnica między zimnym a gorącym startem jest krytyczna dla doświadczenia użytkownika. Przy zimnym starciu deweloper musi upewnić się, że uruchomienie następuje jak najszybciej — leniwa inicjalizacja modułów, odroczone ładowanie ciężkich zasobów, minimalizacja pracy w głównym wątku na starcie. Google zaleca zimny start nie dłuższy niż 500 ms, Apple — nie dłuższy niż 400 ms dla iOS.

kotlin
// Pomiar czasu zimnego startu w Android
class App : Application() {
    private var startTime: Long = 0L

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

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

// Uruchomienie Activity z leniwą inicjalizacją
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)
        // Tylko niezbędne minimum dla pierwszej klatki
        setupNavigation()
    }

    override fun onPostCreate(savedInstanceState: Bundle?) {
        super.onPostCreate(savedInstanceState)
        // Ciężka inicjalizacja po renderowaniu
        initializeHeavyModules()
    }
}

W przykładzie pokazano pomiar czasu zimnego startu w Android. Application.onCreate() jest wywoływane przy przejściu z Not Running do Active. Znacznik czasu jest rejestrowany przy starcie procesu. Activity używa leniwej inicjalizacji przez delegat lazy, aby nie blokować pierwszej klatki. onPostCreate to optymalne miejsce do inicjalizacji ciężkich modułów, ponieważ UI jest już narysowany.

Not Running w iOS: Swift i AppDelegate

W iOS Not Running jest zarządzane przez delegata UIApplicationDelegate. Kluczowe metody: application(_:didFinishLaunchingWithOptions:) jest wywoływane po zimnym starcie, applicationWillTerminate(_:) jest wywoływane przed zakończeniem aplikacji przez użytkownika. System może jednak zakończyć aplikację bez wywołania applicationWillTerminate — na przykład przy awaryjnym zakończeniu lub zwolnieniu pamięci. iOS nie gwarantuje wywołania tej metody, dlatego dane należy zapisywać w applicationDidEnterBackground.

Scenariusze przejścia do Not Running na iOS

Użytkownik może ręcznie zakończyć aplikację przesunięciem w App Switcher. System może zwolnić aplikację z pamięci w tle. Aplikacja może zakończyć się awaryjnie (crash). We wszystkich przypadkach wszystkie obiekty utworzone podczas uruchamiania są niszczone. Stan, który nie został zapisany, jest tracony bezpowrotnie. W iOS 13+ do zapisywania stanu zaleca się używanie NSUserActivity lub mechanizmu state restoration przez UIApplication.stateRestorationIdentifier.

swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    // Zimny start: aplikacja przeszła z Not Running
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // Inicjalizacja minimalnego zestawu usług
        setupAnalytics()
        configureAppearance()
        return true
    }

    // Aplikacja kończy działanie — tylko ręczne zamknięcie
    func applicationWillTerminate(
        _ application: UIApplication
    ) {
        saveCriticalData()
    }

    // Zapisywanie danych przed przejściem w tło
    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
        )
    }
}

Kod pokazuje prawidłową obsługę Not Running na iOS. applicationWillTerminate jest wywoływane tylko przy ręcznym zakończeniu przez użytkownika. Zapisywanie krytycznych danych jest powielone w applicationDidEnterBackground, ponieważ ta metoda jest gwarantowanie wywoływana przed przejściem w tło. State restoration pozwala zachować stos UI do późniejszego przywrócenia przy zimnym starcie.

Not Running w Android: Kotlin i proces

W Android Not Running oznacza, że proces aplikacji nie istnieje. System Linux, na którym oparty jest Android, zarządza procesami przez mechanizm Zygote. Przy uruchomieniu aplikacji Zygote forka nowy proces, ładuje Dalvik/ART i wywołuje Application.onCreate(). W Android nie ma bezpośredniego odpowiednika applicationWillTerminate — system może zakończyć proces w dowolnym momencie bez ostrzeżenia.

Cykl życia procesu Android

Gdy Activity jest wywoływane po raz pierwszy, system tworzy proces, Application i Activity przez łańcuch onCreate → onStart → onResume. Jeśli użytkownik naciśnie Back, Activity jest niszczone (onDestroy), a proces może zostać zakończony przez system. Kluczowa różnica w stosunku do iOS: w Android proces może nadal istnieć nawet bez aktywnych Activity — na przykład jeśli działa Foreground Service lub jest aktywny BroadcastReceiver.

kotlin
// Obsługa Not Running przez SavedStateHandle w 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 — pierwsze wywołanie zwrotne po Not Running
class MyApplication : Application() {

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

SavedStateHandle — komponent Android Architecture Components, który automatycznie zapisuje stan przy przejściu do Not Running i przywraca go przy zimnym starcie. ViewModel utworzony przez ViewModelProvider przetrwa obrót ekranu i zniszczenie Activity. Przy zakończeniu procesu dane z SavedStateHandle są serializowane do Bundle i zapisywane w saved instance state.

Przyczyny przejścia do Not Running

Not Running następuje z kilku powodów. Użytkownik ręcznie zamyka aplikację. System zwalnia aplikację przy braku pamięci. Aplikacja kończy się awaryjnie z wyjątkiem. W Android system może zakończyć proces przy masowej aktualizacji aplikacji lub restarcie urządzenia. iOS może zakończyć aplikację przy przekroczeniu limitu czasu zadania w tle (zwykle 30 sekund).

PrzyczynaiOSAndroidMożliwość zapobieżenia
Ręczne zamknięcie przez użytkownikaPrzesunięcie w App SwitcherPrzesunięcie z RecentsNie — działanie użytkownika
Brak pamięciWyzwolenie memory warningonTrimMemory / LMKCzęściowo — optymalizacja pamięci
Crash aplikacjiNSException / sygnałUncaughtException / ANRTak — obsługa błędów i crash-reporting
Limit czasu zadania w tle30 sek na Background task10 min na JobSchedulerTak — prawidłowe planowanie zadań
Restart OSWywołanie applicationWillTerminateBroadcast ACTION_SHUTDOWNNie — zdarzenie systemowe
Aktualizacja aplikacjiNie występuje (iOS Sandbox)Proces kończy się przy aktualizacji APKNie — aktualizacja systemowa

Jak diagnozować przejście do Not Running

Dla iOS używaj logowania konsolowego w applicationWillTerminate i applicationDidFinishLaunching. Dodaj flagę w UserDefaults przy każdym uruchomieniu — jeśli przy następnym starcie flagi nie ma, aplikacja została zakończona nieprawidłowo. W Android używaj ActivityManager.isBackgroundRestricted(), aby sprawdzić, czy aplikacja może uruchamiać zadania w tle. Śledź również onTrimMemory(TRIM_MEMORY_COMPLETE) — to sygnał, że proces zostanie zakończony.

Najlepsze praktyki pracy z Not Running

Pierwsza zasada — nigdy nie zakładaj, że applicationWillTerminate lub onDestroy zostaną wywołane. Zapisz krytycznie ważne dane przy każdym przejściu z Active do Background. Używaj magazynów klucz-wartość dla prostych ustawień i SQLite/Room dla danych strukturalnych.

Druga zasada — mierz czas zimnego startu i optymalizuj go. Leniwa inicjalizacja, minimalizacja pracy w głównym wątku, wstępne ładowanie zasobów, używanie SplashScreen API — wszystko to poprawia odbiór czasu uruchamiania. Google zaleca zimny start poniżej 200 ms dla doskonałego UX.

Trzecia zasada — zaimplementuj State Restoration. W iOS używaj UIApplication.stateRestorationIdentifier i NSUserActivity. W Android używaj SavedStateHandle w ViewModel w połączeniu z onSaveInstanceState. Pozwoli to użytkownikowi kontynuować pracę od tego samego miejsca po restarcie aplikacji.

Czwarta zasada — obsługuj launchOptions i Intent, z którymi aplikacja została uruchomiona po Not Running. Deep linki, powiadomienia push, linki uniwersalne — wszystkie są przekazywane przez parametry uruchomienia. Deweloper musi prawidłowo wyodrębnić te dane i skierować użytkownika na odpowiedni ekran.

swift
// Obsługa deep link po zimnym starcie
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    // Sprawdzenie, czy przyszło powiadomienie
    if let notification = launchOptions?[.remoteNotification] as? [String: Any] {
        handleNotification(notification)
    }
    // Sprawdzenie 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)
}

Kod pokazuje obsługę parametrów uruchomienia przy zimnym starcie iOS. launchOptions zawiera dane, z którymi system uruchomił aplikację. Powiadomienia, deep linki i linki uniwersalne są przekazywane przez ten słownik. Deweloper musi prawidłowo obsłużyć wszystkie możliwe scenariusze uruchomienia, aby zapewnić bezproblemowe doświadczenie użytkownika.

Często zadawane pytania

Co dzieje się z danymi przy przejściu do Not Running?

Dane, które zostały zapisane w stałym magazynie (UserDefaults, Core Data, SharedPreferences, Room), są zachowywane. Dane w pamięci RAM — zmienne, cache, stan ViewModel bez SavedStateHandle — są tracone bezpowrotnie. Dlatego krytycznie ważne jest zapisywanie stanu aplikacji przy każdym przejściu w tło.

Jak odróżnić zimny start od gorącego na iOS?

Przy zimnym starcie wywoływane jest application(_:didFinishLaunchingWithOptions:). Przy gorącym starcie (powrót z Suspended) ta metoda nie jest wywoływana — działają tylko applicationWillEnterForeground i applicationDidBecomeActive. Jeśli chcesz wykonać akcję tylko przy zimnym starcie, ustaw flagę w didFinishLaunchingWithOptions.

Czy aplikacja Android może być w Not Running z aktywnym Service?

Tak. Foreground Service ze stałym powiadomieniem zapobiega zakończeniu procesu przez system, nawet jeśli wszystkie Activity są zniszczone. Background Service (startService bez foreground) może zostać zatrzymany przez system w dowolnym momencie. Działający Service oznacza, że proces istnieje i nie jest to już Not Running.

Jak emulować Not Running na symulatorze?

Na symulatorze iOS zakończ aplikację przez App Switcher (Cmd+Shift+H dwa razy, przesuń w górę). Na emulatorze Android użyj adb shell am force-stop com.example.app lub przycisku Stop w Logcat. Następnie uruchom aplikację ponownie — będzie to czysty zimny start z Not Running.

Czym jest kill-switch w kontekście Not Running?

Kill-switch — polecenie serwerowe awaryjnego zakończenia aplikacji. Używane w aplikacjach bankowych i korporacyjnych do zdalnego blokowania dostępu. Jeśli aplikacja otrzymała polecenie kill, przy następnym zimnym starcie blokuje UI i żąda ponownej autoryzacji. W iOS kill-switch jest implementowany przez remote notifications z flagą blokady.

Podsumowanie

  • Not Running — początkowy i końcowy stan cyklu życia, aplikacja nie jest załadowana do pamięci i nie wykonuje kodu
  • Zimny start — pełne przeładowanie aplikacji z Not Running, wymaga inicjalizacji wszystkich komponentów od zera
  • Gorący start — powrót z Suspended, nie wywołuje didFinishLaunchingWithOptions ani Application.onCreate
  • Zapisywanie danych — krytycznie ważne przy przejściu w tło, ponieważ Not Running może nastąpić w dowolnym momencie
  • iOS — applicationWillTerminate nie jest gwarantowane, stan jest zapisywany przez UserDefaults lub state restoration
  • Android — proces może zostać zakończony w dowolnym momencie, SavedStateHandle w ViewModel zapisuje stan automatycznie
  • Optymalizacja startu — leniwa inicjalizacja, minimalna praca w main thread, SplashScreen API dla szybkiej pierwszej klatki

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również