Головне
App Lifecycle — це скінченний автомат, який описує всі можливі стани мобільного застосунку та переходи між ними. Кожен стан визначає, чи може застосунок виконувати код, відображати інтерфейс користувача та споживати ресурси. iOS та Android мають подібну, але не ідентичну модель — відмінності зумовлені архітектурою ОС: iOS використовує суворий контроль пам'яті (стан suspended), Android — гнучке управління процесами через onSaveInstanceState.
В iOS життєвий цикл жорстко прив'язаний до переднього/заднього плану: застосунок або активний, або призупинений. В Android життєвий цикл більш гранулярний — Activity/вікно проходить через 6+ станів, а сам процес має додатковий життєвий цикл через ProcessLifecycleOwner. Сучасні застосунки (з 2024 року) все частіше використовують архітектурні патерни на основі життєвого циклу: SwiftUI Lifecycle (iOS 16+) через середовище .scenePhase, Compose Lifecycle через LifecycleEventObserver. Ці патерни автоматично обробляють перемикання контексту та запобігають витокам пам'яті. За даними Google, коректна обробка життєвого циклу в Android знижує споживання пам'яті на 30–40 % під час фонової роботи.
В iOS життєвий цикл застосунку керується через клас UIApplicationDelegate (AppDelegate). Основні методи: application(_:didFinishLaunchingWithOptions:) — ініціалізація при першому запуску; applicationDidBecomeActive — застосунок видимий і готовий до взаємодії; applicationWillResignActive — перехід у неактивний стан (вхідний дзвінок, закриття шторки сповіщень); applicationDidEnterBackground — застосунок приховано, є ~5 секунд на збереження даних; applicationWillEnterForeground — повернення з фону; applicationWillTerminate — завершення (викликається лише для старих застосунків без suspended).
Починаючи з iOS 13, Apple ввела SceneDelegate (UISceneDelegate) для підтримки багатовіконних застосунків на iPad та macOS Catalyst. SceneDelegate керує життєвим циклом кожної окремої сцени (вікна): scene(_:willConnectTo:options:) — створення сцени; sceneDidBecomeActive — сцена активна; sceneWillResignActive — сцена втрачає фокус; sceneDidEnterBackground — сцена прихована. AppDelegate відповідає за глобальні події (завантаження даних, push-сповіщення), а SceneDelegate — за стан інтерфейсу користувача кожного вікна. В SwiftUI життєвий цикл обробляється через значення середовища @Environment(\.scenePhase): .active, .inactive, .background — це забезпечує єдиний API для застосунків SwiftUI та UIKit.
В Android життєвий цикл починається з класу Application — синглтона, який створюється до будь-якої Activity. Метод Application.onCreate() — перша точка входу, використовується для глобальної ініціалізації (Analytics, DI, Crash Reporting). Після цього запускається Activity з послідовністю: onCreate() → onStart() → onResume(). При згортанні: onPause() → onStop() → onSaveInstanceState(). При знищенні: onDestroy(). При поверненні: onRestart() → onStart() → onResume().
ProcessLifecycleOwner (з AndroidX Lifecycle 2.2+) відстежує життєвий цикл всього процесу, а не окремої Activity. Він надає два стани: ON_RESUME (застосунок на передньому плані) та ON_STOP (застосунок приховано). ProcessLifecycleOwner вирішує проблему «коли застосунок пішов у фон» на рівні процесу — це критично для SDK, бібліотек та модульних застосунків. В Jetpack Compose життєвий цикл керується через LifecycleEventObserver та collectAsStateWithLifecycle, який автоматично призупиняє збір даних у фоновому режимі. Android 14 (API 34) додав сповіщення про життєвий цикл служби переднього плану через Service.onTimeout — служба має завершитися за 6 годин, інакше система зупиняє її примусово.
| iOS | Android | Опис |
|---|---|---|
| Not Running | — | Застосунок не запущено |
| didFinishLaunching | Application.onCreate | Перша ініціалізація при запуску |
| Active | onResume | Застосунок видимий і приймає введення |
| Inactive | onPause | Тимчасова втрата фокусу (дзвінок, шторка сповіщень) |
| Background | onStop | Застосунок приховано, код може виконуватися |
| Suspended | — | Код не виконується, пам'ять зарезервована |
| Will Terminate | onDestroy | Застосунок завершується |
Основна відмінність: iOS примусово переводить застосунок у Suspended (код не виконується) через 5-10 секунд після переходу в фон. Android дозволяє фонову роботу (служби, WorkManager) протягом довшого часу, але з обмеженнями Doze Mode (Android 6+) та фонових обмежень (Android 12+). ProcessLifecycleOwner в Android — аналог UIApplicationDidEnterBackgroundNotification в iOS з тією відмінністю, що він спрацьовує при будь-якому переході в фон, а не лише після втрати фокусу.
Базова реалізація AppDelegate з обробкою всіх станів життєвого циклу. Збереження даних при переході в фон та оновлення інтерфейсу при поверненні.
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 — центральна точка життєвого циклу. applicationDidEnterBackground — критичний метод: iOS дає ~5 секунд на збереження. applicationDidBecomeActive — оновіть інтерфейс, перезапустіть анімації. Для SceneDelegate (iOS 13+) логіка життєвого циклу розподіляється по сценах: sceneDidBecomeActive / sceneDidEnterBackground керують окремими вікнами.
SceneDelegate для багатовіконних застосунків iPad. Кожна сцена має власний життєвий цикл, незалежний від інших вікон.
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 керує життєвим циклом окремої сцени. sceneDidBecomeActive / sceneDidEnterBackground — аналоги методів AppDelegate для конкретного вікна. sceneDidDisconnect викликається при закритті сцени (користувач закрив вікно на iPad). Для збереження сумісності з iOS 12 та нижче, AppDelegate має дублювати логіку SceneDelegate. В iOS 17+ можна використовувати @Observable для автоматичного призупинення/відновлення завдань на основі scenePhase.
ProcessLifecycleOwner відстежує, коли застосунок знаходиться на передньому/задньому плані на рівні процесу. Це оптимальний спосіб для глобального керування життєвим циклом.
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 — єдиний коректний спосіб відстежити перехід у фон на рівні застосунку. ON_START спрацьовує, коли застосунок стає видимим (передній план), ON_STOP — коли повністю приховано (фон). На відміну від життєвого циклу Activity, ProcessLifecycleOwner не прив'язаний до конкретного екрана. Для Jetpack Compose використовуйте LifecycleResumeEffect або collectAsStateWithLifecycle замість ручного спостереження.
ViewModel + Lifecycle — правильна архітектура Android для роботи з життєвим циклом. ViewModel автоматично очищається при onDestroy, запобігаючи витокам.
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 — звільнення ресурсів")
}
}
// В 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 — призупинення анімацій")
}
override fun onDestroy() {
super.onDestroy()
Logger.d("Activity destroyed — очищення")
}
}
ViewModel переживає поворот екрана та знищується лише при завершенні Activity. onCleared() — місце для звільнення ресурсів та відписки від потоків. LiveData автоматично призупиняє спостереження при onPause та відновлює при onResume — це забезпечує захист від витоків та збоїв під час фонової роботи. В Compose використовуйте collectAsStateWithLifecycle() для тієї ж мети.
Часті запитання
При згортанні застосунок переходить з active в inactive (короткочасно), потім у background. Через кілька секунд система може перевести його в suspended — код не виконується, пам'ять зарезервована. При нестачі пам'яті система завершує призупинений застосунок (willTerminate не викликається). Зберігайте дані в applicationDidEnterBackground — це остання гарантована точка для збереження.
Мінімально обов'язкові: applicationDidEnterBackground — зберегти дані користувача; applicationWillEnterForeground — оновити інтерфейс; в Android onPause — призупинити анімації/сенсори. ProcessLifecycleOwner спрощує обробку: onResume — onPause для переднього плану, onStart — onStop для видимості. Інші стани обробляйте за необхідності — не всі стани критичні для кожного застосунку.
AppDelegate — глобальний делегат всього застосунку (запуск, перехід у фон). SceneDelegate (iOS 13+) керує життєвим циклом окремого вікна (сцени) — в багатовіконних iPad-застосунках кожна сцена має свій життєвий цикл. Для iOS 12 та нижче весь життєвий цикл обробляється через AppDelegate. З iPadOS 16+ SceneDelegate обов'язковий для Stage Manager.
ProcessLifecycleOwner — компонент з AndroidX Lifecycle, який відстежує життєвий цикл всього застосунку (процесу), а не окремої Activity. Він надає дві основні події: ON_RESUME (застосунок на передньому плані) та ON_STOP (застосунок у фоні). Використовується для ініціалізації/очищення SDK, аналітики, мережевих моніторів — всього, що має реагувати на перехід застосунку в фон, незалежно від поточного екрана.
В Jetpack Compose використовуйте LifecycleEventObserver через LocalLifecycleOwner.current.lifecycle або функцію collectAsStateWithLifecycle(). Для Compose-екранів застосовуйте LifecycleResumeEffect — аналог DisposableEffect, який запускається при onResume та очищається при onPause. lifecycleScope.launchWhenResumed — корутина, яка автоматично призупиняється у фоні.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.