Мобильная разработка
Жизненный цикл мобильных приложений
Цели урока
- Понимать состояния lifecycle (Not Running, Active, Inactive, Background, Suspended) и переходы между ними
- Применять паттерн: обновление данных в onResume, сохранение в onPause
- Реализовывать state restoration через SavedStateHandle (Android) и @SceneStorage (iOS)
- Правильно работать с background tasks: beginBackgroundTask (iOS), WorkManager (Android)
Предварительные знания
Черновик письма набирается 10 минут. Звонок. Разговор. Возвращение - черновик исчез. Или навигатор перестроил маршрут, потому что выключился экран. Или банковское приложение потеряло заполненную форму. Всё это - приложения, которые не знали, что ОС может убить их в любой момент. Lifecycle - не абстракция из документации, а правила выживания в среде с ограниченными ресурсами.
- **Навигаторы** используют background location mode для отслеживания маршрута, даже когда экран выключен
- **Мессенджеры** получают push notifications через APNs/FCM и будят приложение для обработки входящих сообщений
- **Банковские приложения** используют state restoration для сохранения заполненных форм перевода при сворачивании
Как мобильные ОС научились убивать приложения
Первый iPhone (2007) вообще не поддерживал сторонние приложения - только веб-приложения через Safari. С iOS 2.0 (2008) появился App Store и нативные приложения, но строгого lifecycle не было. iOS 4 (2010) ввёл multitasking - приложения начали уходить в background. Именно тогда возникли первые проблемы: батарея садилась за 4 часа, потому что разработчики не завершали фоновые задачи. Apple ответила жёсткими ограничениями. Android шёл параллельным путём: Activity lifecycle появился с первыми версиями (2008), но Doze mode, App Standby и фоновые ограничения появились только в Android 6.0 Marshmallow (2015) и ужесточались в каждом релизе.
Lifecycle
**Пользователь набирает длинное сообщение, звонит телефон, после разговора он возвращается - сообщение исчезло.** Это не баг операционной системы. Это следствие того, что разработчик не обработал lifecycle. Мобильная ОС в любой момент может приостановить приложение, отправить в фон или убить при нехватке памяти - lifecycle определяет, через какие состояния проходит приложение и когда вызываются callback-методы.
**Жизненный цикл мобильного приложения** - набор состояний и переходов между ними: - **Not Running** - приложение не запущено - **Active (Foreground)** - на экране, получает события - **Inactive** - видимо, но не получает события (входящий звонок) - **Background** - не видимо, выполняет ограниченные задачи - **Suspended / Terminated** - заморожено или убито ОС
На iOS lifecycle управляется через **UIApplicationDelegate** (или **SwiftUI App** protocol) и **UISceneDelegate**. На Android - через callback-методы **Activity**: onCreate, onStart, onResume, onPause, onStop, onDestroy.
Ключевое различие платформ: на Android каждая Activity имеет свой lifecycle, и система уничтожает Activity при повороте экрана (configuration change). На iOS lifecycle привязан к сценам (scenes), и поворот экрана не уничтожает ViewController.
Что происходит, когда пользователь сворачивает приложение на iOS?
Foreground
**Instagram открывается за 200 мс и сразу показывает свежую ленту.** За этим стоит паттерн: данные кешированы, при переходе в foreground запускается фоновое обновление, UI не блокируется ни на миллисекунду. Foreground - «золотое время» приложения с доступом ко всем ресурсам: сеть, GPU, GPS, камера.
**В foreground приложение может:** - Обновлять UI в реальном времени (60/120 FPS) - Делать сетевые запросы без ограничений - Использовать GPS, камеру, микрофон - Получать touch/gesture события - Воспроизводить аудио/видео **Обязанности в foreground:** - Не блокировать main thread (UI зависнет) - Загружать данные асинхронно - Обрабатывать ошибки сети gracefully
**Главное правило foreground: никогда не блокируй main thread.** Сетевые запросы, обращения к БД, тяжёлые вычисления - всё на background threads. Если main thread заблокирован более 5 секунд, Android покажет диалог «Application Not Responding» (ANR), а iOS может убить приложение.
| Событие | iOS callback | Android callback |
|---|---|---|
| Приложение стало видимым | sceneDidBecomeActive | onResume() |
| Приложение теряет фокус | sceneWillResignActive | onPause() |
| Появление в окне | viewDidAppear | onStart() |
| Исчезновение из окна | viewDidDisappear | onStop() |
Типичный паттерн: в onResume/sceneDidBecomeActive - обновляем данные (пользователь мог вернуться спустя часы). В onPause/sceneWillResignActive - сохраняем состояние (пользователь может не вернуться).
Почему нельзя выполнять сетевые запросы на main thread?
Background
**Spotify играет музыку при выключенном экране - это не случайность.** Это явно запрошенный background mode «Audio playback», который iOS разрешает только для конкретных задач. Всё остальное: минимум CPU, запрет обновления UI, 15 секунд на завершение и заморозка. Цель ОС - батарея и память.
**Ограничения в background:** - **iOS:** ~15 секунд на завершение задач, потом приложение замораживается (suspended) - **Android:** Doze mode ограничивает сеть и CPU; WorkManager для отложенных задач - **Обе платформы:** нельзя обновлять UI, ограниченный доступ к GPS **Разрешённые фоновые режимы (iOS):** Audio playback, Location updates, VoIP, Bluetooth, Background fetch, Push notifications
**Push Notifications** - главный механизм «оживления» приложения из фона. Сервер отправляет уведомление через APNs (iOS) или FCM (Android), ОС будит приложение на короткое время для обработки. Так работают мессенджеры, почтовые клиенты и навигаторы.
| Задача | iOS решение | Android решение |
|---|---|---|
| Загрузка файлов в фоне | URLSession background | WorkManager + DownloadManager |
| Периодическая синхронизация | BGAppRefreshTask | PeriodicWorkRequest |
| Воспроизведение аудио | Audio background mode | Foreground Service |
| Трекинг геолокации | Location background mode | Foreground Service + LocationManager |
| Push-уведомления | APNs + UNNotificationServiceExtension | FCM + FirebaseMessagingService |
Сколько времени iOS даёт приложению на выполнение задач после перехода в background?
State Restoration
**Google Maps возвращает на тот же маршрут даже после перезагрузки телефона.** Это не случайность - state restoration. ОС может убить фоновое приложение в любой момент без предупреждения. Когда пользователь вернётся, он ожидает увидеть то же состояние: открытый экран, введённый текст, позицию скролла.
**State Restoration - зачем нужен:** 1. ОС убивает приложение в фоне (нехватка памяти) 2. Android уничтожает Activity при повороте экрана (configuration change) 3. Пользователь возвращается через часы и ожидает тот же экран **Что сохранять:** текущий экран/навигацию, ввод пользователя (черновики), позицию скролла, фильтры/параметры поиска
На Android проблема острее: **configuration change** (поворот экрана, смена языка, переключение тёмной темы) по умолчанию пересоздаёт Activity. ViewModel переживает configuration change, но не process death. Для полного выживания нужен SavedStateHandle.
| Событие | ViewModel жив? | SavedState жив? | Решение |
|---|---|---|---|
| Поворот экрана (Android) | Да | Да | ViewModel достаточно |
| Process death (оба) | Нет | Да | SavedStateHandle / @SceneStorage |
| Удаление из Recent Apps | Нет | Нет | Persistent storage (БД, файлы) |
| Переустановка приложения | Нет | Нет | Cloud sync / серверное хранение |
Lifecycle мобильного приложения - это постоянная борьба с непредсказуемостью ОС. Приложение может быть убито в любой момент. Хорошее приложение готово к этому: сохраняет состояние, корректно обрабатывает background и возвращает пользователя точно туда, где он остановился.
Приложение живёт вечно в фоне и может делать что угодно
ОС агрессивно ограничивает и убивает фоновые приложения. iOS замораживает через ~15 секунд, Android применяет Doze mode и App Standby Buckets. Приложение должно быть готово к уничтожению в любой момент
Мобильные устройства имеют ограниченную батарею и память. Если бы 50 приложений работали в фоне постоянно, телефон разряжался бы за час. ОС оптимизирует ресурсы, жертвуя фоновыми приложениями.
На Android поворот экрана по умолчанию вызывает:
Ключевые идеи
- **Lifecycle** - набор состояний (Active, Inactive, Background, Suspended) с callback-методами на каждом переходе
- **Foreground:** полный доступ к ресурсам, но нельзя блокировать main thread - иначе ANR/зависание
- **Background:** жёсткие ограничения (~15 с iOS, Doze Android); используй WorkManager/BGTaskScheduler для надёжных задач
- **State restoration:** ОС может убить приложение без предупреждения; сохраняй состояние через SavedStateHandle/@SceneStorage
Связанные темы
Lifecycle влияет на всё в мобильной разработке:
- Нативная vs кроссплатформенная — Lifecycle одинаков для всех подходов - даже Flutter/RN приложения проходят через те же состояния ОС
- Swift и SwiftUI — SwiftUI упрощает lifecycle через @SceneStorage, @Environment(\.scenePhase), но под капотом те же правила iOS
Вопросы для размышления
- Почему iOS ограничивает фоновое выполнение 15 секундами, а не 5 минутами?
- Как реализовать оффлайн-режим для мессенджера, учитывая что ОС может убить приложение?
- Чем подход Android (пересоздание Activity при повороте) лучше или хуже подхода iOS (сохранение ViewController)?
Связанные уроки
- mob-01 — Нативная vs кроссплатформенная разработка - контекст для понимания lifecycle iOS и Android
- mob-03 — Swift и SwiftUI используют @SceneStorage и scenePhase - прямо из lifecycle
- mob-04 — Kotlin и Jetpack Compose строятся на Activity lifecycle как фундаменте
- se-08 — State management паттерны - ViewModel, SavedState - применяются прямо к lifecycle
- ds-01 — OS как distributed system: приложение - нода, lifecycle - протокол согласования ресурсов
- devops-05 — Observability: crashes и ANR из lifecycle - первое что смотрит production monitoring
- os-03-threads