Najważniejsze
App Lifecycle — automat stanów opisujący wszystkie możliwe stany aplikacji mobilnej i przejścia między nimi. Każdy stan określa, czy aplikacja może wykonywać kod, wyświetlać UI i zużywać zasoby. iOS i Android mają podobny, ale nie identyczny model — różnice wynikają z architektury systemu: iOS stosuje ścisłą kontrolę pamięci (stan suspended), Android — elastyczne zarządzanie procesami przez onSaveInstanceState.
W iOS lifecycle jest ściśle powiązany z foreground/background: aplikacja jest albo aktywna, albo zawieszona. W Android lifecycle jest bardziej granularny — Activity/Window przechodzi przez 6+ stanów, a sam proces ma dodatkowy lifecycle przez ProcessLifecycleOwner. Nowoczesne aplikacje (od 2024 roku) coraz częściej używają wzorców architektonicznych opartych na lifecycle: SwiftUI Lifecycle (iOS 16+) przez .scenePhase environment, Compose Lifecycle przez LifecycleEventObserver. Te wzorce automatycznie obsługują przełączanie kontekstu i zapobiegają wyciekom pamięci. Według Google, prawidłowa obsługa lifecycle w Android zmniejsza zużycie pamięci o 30–40% podczas pracy w tle.
W iOS lifecycle aplikacji jest zarządzany przez klasę UIApplicationDelegate (AppDelegate). Główne metody: application(_:didFinishLaunchingWithOptions:) — inicjalizacja przy pierwszym uruchomieniu; applicationDidBecomeActive — aplikacja widoczna i gotowa do interakcji; applicationWillResignActive — przejście w stan nieaktywny (połączenie przychodzące, zamknięcie zasłony); applicationDidEnterBackground — aplikacja ukryta, jest ~5 sekund na zapisanie danych; applicationWillEnterForeground — powrót z tła; applicationWillTerminate — zakończenie (wywoływane tylko dla starych aplikacji bez suspended).
Począwszy od iOS 13, Apple wprowadziło SceneDelegate (UISceneDelegate) do obsługi aplikacji wielookienkowych na iPad i macOS Catalyst. SceneDelegate zarządza lifecycle każdej pojedynczej sceny (okna): scene(_:willConnectTo:options:) — utworzenie sceny; sceneDidBecomeActive — scena aktywna; sceneWillResignActive — scena traci fokus; sceneDidEnterBackground — scena ukryta. AppDelegate odpowiada za zdarzenia globalne (ładowanie danych, push-notifications), a SceneDelegate — za stan UI każdego okna. W SwiftUI lifecycle jest obsługiwany przez wartość środowiska @Environment(\.scenePhase): .active, .inactive, .background — zapewnia to jednolite API dla aplikacji SwiftUI i UIKit.
W Android lifecycle zaczyna się od klasy Application — singletona, który jest tworzony przed dowolną Activity. Metoda Application.onCreate() — pierwszy punkt wejścia, używany do globalnej inicjalizacji (Analytics, DI, Crash Reporting). Następnie uruchamiana jest Activity z sekwencją: onCreate() → onStart() → onResume(). Przy zwijaniu: onPause() → onStop() → onSaveInstanceState(). Przy zniszczeniu: onDestroy(). Przy powrocie: onRestart() → onStart() → onResume().
ProcessLifecycleOwner (z AndroidX Lifecycle 2.2+) śledzi lifecycle całego procesu, a nie pojedynczej Activity. Zapewnia dwa stany: ON_RESUME (aplikacja w foreground) i ON_STOP (aplikacja ukryta). ProcessLifecycleOwner rozwiązuje problem "kiedy aplikacja poszła w tło" na poziomie procesu — jest to krytyczne dla SDK, bibliotek i aplikacji modułowych. W Jetpack Compose lifecycle jest zarządzany przez LifecycleEventObserver i collectAsStateWithLifecycle, który automatycznie wstrzymuje zbieranie danych w trybie tła. Android 14 (API 34) dodał powiadomienie o foreground service lifecycle przez Service.onTimeout — usługa musi zakończyć się w ciągu 6 godzin, w przeciwnym razie system zatrzymuje ją przymusowo.
| iOS | Android | Opis |
|---|---|---|
| Not Running | — | Aplikacja nie jest uruchomiona |
| didFinishLaunching | Application.onCreate | Pierwsza inicjalizacja przy uruchomieniu |
| Active | onResume | Aplikacja widoczna i przyjmuje dane wejściowe |
| Inactive | onPause | Przejściowa utrata fokusu (połączenie, zasłona) |
| Background | onStop | Aplikacja ukryta, kod może być wykonywany |
| Suspended | — | Kod nie jest wykonywany, pamięć zarezerwowana |
| Will Terminate | onDestroy | Aplikacja jest zamykana |
Główna różnica: iOS przymusowo przełącza aplikację w stan Suspended (kod nie jest wykonywany) po 5-10 sekundach od przejścia w tło. Android pozwala na pracę w tle (usługi, WorkManager) przez dłuższy czas, ale z ograniczeniami Doze Mode (Android 6+) i ograniczeniami tła (Android 12+). ProcessLifecycleOwner w Android — odpowiednik UIApplicationDidEnterBackgroundNotification w iOS z tą różnicą, że wyzwala się przy każdym przejściu w tło, a nie tylko po utracie fokusu.
Podstawowa implementacja AppDelegate z obsługą wszystkich stanów cyklu życia. Zapisywanie danych przy przejściu w tło i aktualizacja UI przy powrocie.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Analytics.shared.initialize()
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
NotificationCenter.default.post(name: .appDidBecomeActive, object: nil)
}
func applicationWillResignActive(_ application: UIApplication) {
NotificationCenter.default.post(name: .appWillResignActive, object: nil)
}
func applicationDidEnterBackground(_ application: UIApplication) {
CoreDataManager.shared.saveContext()
UserDefaults.standard.synchronize()
}
func applicationWillEnterForeground(_ application: UIApplication) {
NetworkManager.shared.refreshSession()
}
}
AppDelegate — centralny punkt lifecycle. applicationDidEnterBackground — krytyczna metoda: iOS daje ~5 sekund na zapisanie danych. applicationDidBecomeActive — zaktualizuj UI, uruchom ponownie animacje. Dla SceneDelegate (iOS 13+) logika lifecycle jest rozdzielona na sceny: sceneDidBecomeActive / sceneDidEnterBackground zarządzają poszczególnymi oknami.
SceneDelegate dla aplikacji wielookienkowych iPad. Każda scena ma własny lifecycle, niezależny od innych okien.
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
}
func sceneDidBecomeActive(_ scene: UIScene) {
PlayerManager.shared.resume()
}
func sceneDidEnterBackground(_ scene: UIScene) {
PlayerManager.shared.pause()
CoreDataManager.shared.saveContext()
}
func sceneDidDisconnect(_ scene: UIScene) {
PlayerManager.shared.cleanup()
}
}
SceneDelegate zarządza lifecycle pojedynczej sceny. sceneDidBecomeActive / sceneDidEnterBackground — odpowiednik metod AppDelegate dla konkretnego okna. sceneDidDisconnect jest wywoływane przy zamknięciu sceny (użytkownik zamknął okno na iPad). Dla zachowania zgodności z iOS 12 i starszymi, AppDelegate powinien dublować logikę SceneDelegate. W iOS 17+ można użyć @Observable do automatycznego wstrzymywania/wznawiania zadań na podstawie scenePhase.
ProcessLifecycleOwner śledzi, kiedy aplikacja znajduje się w foreground/background na poziomie procesu. Jest to optymalny sposób globalnego zarządzania lifecycle.
import android.app.Application
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleObserver())
}
}
class AppLifecycleObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onEnterForeground() {
Analytics.shared.onForeground()
NetworkMonitor.shared.start()
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onEnterBackground() {
Analytics.shared.onBackground()
NetworkMonitor.shared.stop()
}
}
ProcessLifecycleOwner — jedyny poprawny sposób śledzenia przejścia w tło na poziomie aplikacji. ON_START wyzwala się, gdy aplikacja staje się widoczna (foreground), ON_STOP — gdy jest całkowicie ukryta (background). W przeciwieństwie do Activity lifecycle, ProcessLifecycleOwner nie jest powiązany z konkretnym ekranem. Dla Jetpack Compose używaj LifecycleResumeEffect lub collectAsStateWithLifecycle zamiast ręcznego obserwowania.
ViewModel + Lifecycle — prawidłowa architektura Android do pracy z lifecycle. ViewModel automatycznie czyści się przy onDestroy, zapobiegając wyciekom.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
class TimerViewModel : ViewModel() {
private val _time = MutableLiveData<Long>()
val time: LiveData<Long> = _time
private var startTime = System.currentTimeMillis()
init {
updateTime()
}
private fun updateTime() {
_time.value = System.currentTimeMillis() - startTime
}
override fun onCleared() {
super.onCleared()
Logger.d("ViewModel cleared — zwalnianie zasobów")
}
}
// W Activity
class TimerActivity : AppCompatActivity() {
private val viewModel: TimerViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Logger.d("Activity created")
}
override fun onResume() {
super.onResume()
viewModel.time.observe(this) { Logger.d("Time: $it") }
}
override fun onPause() {
super.onPause()
Logger.d("Activity paused — wstrzymanie animacji")
}
override fun onDestroy() {
super.onDestroy()
Logger.d("Activity destroyed — czyszczenie")
}
}
ViewModel przetrwa obrót ekranu i jest niszczony tylko przy finish() Activity. onCleared() — miejsce do zwalniania zasobów, wypisywania się ze strumieni. LiveData automatycznie wstrzymuje obserwację przy onPause i wznawia przy onResume — zapewnia to ochronę przed wyciekami i awariami podczas pracy w tle. W Compose używaj collectAsStateWithLifecycle() do tego samego celu.
Często zadawane pytania
Podczas zwijania aplikacja przechodzi z active do inactive (krótkotrwale), następnie do background. Po kilku sekundach system może przełączyć ją w suspended — kod nie jest wykonywany, pamięć jest zarezerwowana. Przy braku pamięci suspended-aplikacja jest terminowana (willTerminate nie jest wywoływane). Zapisz dane w applicationDidEnterBackground — to ostatni gwarantowany punkt do zapisu.
Minimalnie obowiązkowe: applicationDidEnterBackground — zapisz dane użytkownika; applicationWillEnterForeground — zaktualizuj UI; w Android onPause — wstrzymaj animacje/czujniki. ProcessLifecycleOwner upraszcza obsługę: onResume — onPause dla foreground, onStart — onStop dla widoczności. Pozostałe stany obsługuj w miarę potrzeb — nie wszystkie stany są krytyczne dla każdej aplikacji.
AppDelegate — globalny delegat całej aplikacji (uruchomienie, przejście w tło). SceneDelegate (iOS 13+) zarządza lifecycle pojedynczego okna (sceny) — w wielookienkowych aplikacjach iPad każda scena ma własny lifecycle. Dla iOS 12 i starszych cały lifecycle jest obsługiwany przez AppDelegate. Od iPadOS 16+ SceneDelegate jest obowiązkowy dla Stage Manager.
ProcessLifecycleOwner — komponent z AndroidX Lifecycle, który śledzi lifecycle całej aplikacji (procesu), a nie pojedynczej Activity. Zapewnia dwa główne zdarzenia: ON_RESUME (aplikacja w foreground) i ON_STOP (aplikacja w background). Jest używany do inicjalizacji/czyszczenia SDK, analityki, monitorów sieciowych — wszystkiego, co powinno reagować na przejście aplikacji w tło, niezależnie od bieżącego ekranu.
W Jetpack Compose używaj LifecycleEventObserver przez LocalLifecycleOwner.current.lifecycle lub funkcji collectAsStateWithLifecycle(). Dla ekranów Compose stosuj LifecycleResumeEffect — odpowiednik DisposableEffect, który uruchamia się przy onResume i czyści przy onPause. lifecycleScope.launchWhenResumed — corutina automatycznie wstrzymywana w tle.
Podsumowanie
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.
Przeczytaj również