Фоновые задачи в iOS и Android: ограничения и надёжные подходы
Продвинутое3 минРедакция Innovialabs
Коротко
Обе ОС не гарантируют точного времени выполнения фоновых задач, поэтому надёжная синхронизация строится на системных планировщиках, фоновых сессиях загрузки, идемпотентных операциях и досинхронизации при каждом открытии приложения.
Главное: фон не гарантирован
И iOS, и Android экономят батарею и поэтому сами решают, когда и сколько приложению работать в фоне. Нельзя рассчитывать, что код выполнится «каждые 15 минут» или «ровно в 9:00». Надёжный подход — относиться к фону как к бонусу:
- ставить задачи через системные планировщики (WorkManager, BGTaskScheduler);
- для передачи файлов использовать фоновые сетевые сессии, которые ведёт сама ОС;
- делать операции идемпотентными и уметь продолжать с места обрыва;
- всегда досинхронизироваться при открытии приложения.
Ограничения Android
- Doze — при неподвижном устройстве с выключенным экраном система откладывает сетевой доступ, будильники и задачи до коротких окон обслуживания.
- App Standby Buckets — чем реже пользователь открывает приложение, тем реже ему разрешают работать в фоне.
- Ограничения на запуск сервисов из фона — обычный фоновый Service надолго не живёт; для долгой видимой пользователю работы нужен foreground service с уведомлением и объявленным типом.
- Оболочки производителей — у ряда OEM есть собственные агрессивные «оптимизаторы батареи», которые убивают процессы сильнее, чем стандартный Android. На таких устройствах приходится просить пользователя добавить приложение в исключения.
Ограничения iOS
- После ухода в фон приложение быстро приостанавливается. Короткую задачу можно завершить, запросив дополнительное время, но это минуты, а не часы.
- BGTaskScheduler запускает
BGAppRefreshTask(короткое обновление) иBGProcessingTask(долгая работа, часто на зарядке). Время запуска выбирает система с учётом того, как часто пользователь открывает приложение. - Silent push (
content-available) может разбудить приложение, но доставка не гарантируется и ограничивается системой. - Если пользователь принудительно закрыл приложение свайпом, большинство фоновых механизмов перестаёт его будить.
Какой инструмент для какой задачи
| Задача | Android | iOS |
|---|---|---|
| Периодическая синхронизация | WorkManager, PeriodicWorkRequest | BGAppRefreshTask |
| Тяжёлая обработка, бэкап | WorkManager с ограничениями (зарядка, Wi-Fi) | BGProcessingTask |
| Загрузка и выгрузка файлов | WorkManager, при необходимости foreground service | Background URLSession |
| Срочное событие с сервера | Push-уведомление (FCM) | Push-уведомление (APNs) |
| Трекинг маршрута, навигация | Foreground service с типом location | Background location mode |
| Обновление по геозонам | Geofencing API | Region monitoring, significant location changes |
WorkManager на практике
WorkManager переживает перезагрузку устройства и сам выбирает подходящий механизм под капотом:
val request = PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.HOURS)
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"sync", ExistingPeriodicWorkPolicy.KEEP, request
)
Уникальное имя задачи защищает от дублей при каждом запуске приложения. Минимальный интервал периодической задачи ограничен системой, и фактический запуск может сдвигаться.
Фоновая геолокация и загрузки
- Геолокация в фоне требует отдельного разрешения, понятного объяснения пользователю и обоснования при ревью в сторах. Используйте минимально достаточную точность: значимые изменения местоположения и геозоны расходуют батарею заметно меньше, чем постоянный GPS.
- Загрузки больших файлов отдавайте системе: на iOS — background
URLSession, которая продолжит передачу даже при приостановленном приложении; на Android — WorkManager с поддержкой докачки. Сервер должен поддерживать возобновляемую загрузку по частям.
Паттерны надёжной синхронизации
- Локальная очередь операций. Действия пользователя сначала пишутся в базу на устройстве, затем отправляются.
- Идемпотентность. У каждой операции есть уникальный ключ, и повторная отправка не создаёт дубль на сервере.
- Инкрементальная синхронизация по курсору или метке времени вместо полной перезагрузки данных.
- Ретраи с экспоненциальной задержкой и учётом состояния сети.
- Push как сигнал, а не как данные: уведомление говорит «есть изменения», а данные приложение забирает само.
- Синхронизация при открытии как обязательная страховка.
- Мониторинг: логируйте, когда задачи реально выполнялись, чтобы видеть проблемы на конкретных устройствах.