Background — стан життєвого циклу додатка, в якому він продовжує виконуватися, але не відображається на екрані. Пояснюємо основи фонової роботи на iOS та Android: обмеження, таймаути, фонові завдання через beginBackgroundTask, WorkManager та Service, а також найкращі практики для коректної обробки Background.
Головне
Background — стан додатка, в якому він продовжує існувати в операційній системі, виконувати код та споживати ресурси, але не відображається на екрані пристрою. Користувач знаходиться на головному екрані, в іншому додатку або екран пристрою заблоковано. На iOS Background слідує за Inactive — ланцюжок переходу: Active → Inactive → Background. На Android onStop сигналізує про перехід Activity у Background.
Обидві платформи накладають суворі обмеження на фонову роботу. iOS надає обмежене вікно (зазвичай 30 секунд) для виконання коду після переходу в Background, після чого додаток переводиться в Suspended. Android більш гнучкий: Foreground Service з видимим повідомленням може працювати необмежено довго, але звичайний Background Service обмежений кількома хвилинами. Ключове завдання розробника — коректно зберегти стан і запланувати продовження роботи через системні API фонових завдань.
Система може завершити фоновий додаток у будь-який момент при нестачі пам'яті. При завершенні всі незбережені дані втрачаються. Тому критично важливо зберігати стан в applicationDidEnterBackground (iOS) або onStop (Android). Після завершення при наступному запуску додаток стартує з Not Running з холодним стартом і відновлює збережений стан.
Важливо розрізняти Background та Suspended. Background — додаток активно виконує код. Suspended — додаток знаходиться в пам'яті, але не виконує код — він заморожений. На iOS додаток переходить з Background в Suspended після завершення фонових завдань. На Android немає Suspended — процес або існує (включаючи Background), або завершений (Not Running). Однак Android може призупинити виконання потоків через LMK (Low Memory Killer).
| Характеристика | iOS Background | Android Background |
|---|---|---|
| Код виконується | Так, до 30 секунд | Так, залежить від API |
| UI видимий | Ні | Ні |
| Таймаут за замовчуванням | ~30 сек (beginBackgroundTask) | Кілька хвилин (Service) |
| Необмежена робота | Тільки спеціальні категорії (аудіо, VoIP, навігація) | Foreground Service з повідомленням |
| Гарантія виконання | Ні — система може завершити в будь-який момент | WorkManager гарантує виконання |
| Потрібен дозвіл | Так — capabilities в Info.plist | Так — FOREGROUND_SERVICE дозвіл |
| Наступний стан | Suspended → Not Running | Not Running (або перезапуск) |
На iOS Background обробляється через метод делегата applicationDidEnterBackground. У цьому методі розробник повинен зберегти стан користувача, звільнити ресурси та завершити фонові завдання. Для виконання коду після переходу в Background використовується beginBackgroundTask(expirationHandler:) — API, який запитує у системи додатковий час (зазвичай 30 секунд). Якщо завдання не завершується за цей час, викликається expirationHandler, і додаток примусово переводиться в Suspended.
З iOS 13 Apple представила BGTaskScheduler — сучасний API для планування фонових завдань. На відміну від beginBackgroundTask, який дає час тільки на завершення після переходу в фон, BGTaskScheduler дозволяє запланувати виконання завдань у майбутньому — наприклад, оновлення контенту раз на годину або вивантаження аналітики вночі. BGTaskScheduler — рекомендований підхід для нових проєктів, оскільки він більш ефективний з точки зору батареї.
import UIKit
import BackgroundTasks
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
// Додаток перейшов у фон — починаємо фонове завдання
func applicationDidEnterBackground(_ application: UIApplication) {
saveAppState()
startBackgroundTask()
}
private func startBackgroundTask() {
backgroundTaskID = UIApplication.shared.beginBackgroundTask { [weak self] in
// Час вийшов — примусово завершуємо
self?.endBackgroundTask()
}
// Симулюємо фонову роботу (збереження даних на сервер)
DispatchQueue.global().async { [weak self] in
uploadAnalyticsData()
self?.endBackgroundTask()
}
}
private func endBackgroundTask() {
guard backgroundTaskID != .invalid else { return }
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
// Реєстрація BGTaskScheduler
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.example.refresh",
using: nil
) { task in
handleAppRefresh(task: task as! BGAppRefreshTask)
}
return true
}
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.example.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 3600)
try? BGTaskScheduler.shared.submit(request)
}
func handleAppRefresh(task: BGAppRefreshTask) {
scheduleAppRefresh()
task.expirationHandler = { task.setTaskCompleted(success: false) }
fetchLatestData { result in
task.setTaskCompleted(success: result)
}
}
}У коді показана повна обробка Background на iOS. applicationDidEnterBackground запускає фонове завдання через beginBackgroundTask з таймаутом та expirationHandler. Паралельно реєструється BGTaskScheduler для періодичного оновлення контенту. beginBackgroundTask використовується для негайного завершення роботи, BGTaskScheduler — для довгострокового планування. Обидва API вимагають правильного управління ідентифікаторами завдань.
На Android Background управляється через кілька API. Традиційний Service дозволяє виконувати код у фоновому режимі, але з Android 8+ (API 26) Background Service обмежений: система завершує його через кілька хвилин після переходу додатка в фон. Foreground Service з постійним повідомленням може працювати необмежено довго. WorkManager — рекомендоване рішення для фонових завдань з гарантією виконання навіть після перезавантаження пристрою.
Android, на відміну від iOS, підтримує довготривалі фонові процеси. Foreground Service використовується для завдань, які користувач повинен бачити — відтворення музики, навігація, запис тренування. JobScheduler та WorkManager — для завдань, які можуть бути відкладені: синхронізація даних, вивантаження логів, оновлення кешу. Ключова відмінність: Android дозволяє планувати завдання з умовами — Wi-Fi, зарядка, неактивність пристрою, що економить батарею та трафік.
import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.work.*
// 1. Foreground Service для довгої фонової роботи
class SyncService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = createNotification()
startForeground(NOTIFICATION_ID, notification)
performBackgroundWork()
return START_STICKY
}
private fun performBackgroundWork() {
Thread {
// Синхронізація даних з сервером
syncDataToServer()
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
}.start()
}
override fun onBind(intent: Intent?): IBinder? = null
}
// 2. WorkManager для відкладених фонових завдань
class DataSyncWorker(
private val context: Context,
private val params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
// Вивантаження аналітики на сервер
uploadAnalytics()
Result.success()
} catch (e: Exception) {
if (runAttemptCount < 3) Result.retry() else Result.failure()
}
}
}
// Планування WorkManager завдання
fun scheduleBackgroundSync(context: Context) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
val request = OneTimeWorkRequestBuilder<DataSyncWorker>()
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
.build()
WorkManager.getInstance(context).enqueue(request)
}У коді показано два підходи до фонової роботи на Android. SyncService — Foreground Service з повідомленням для негайної та довгої фонової роботи. DataSyncWorker — WorkManager для відкладених завдань з умовами (Wi-Fi, зарядка). WorkManager гарантує виконання навіть при перезавантаженні пристрою та підтримує exponential backoff для повторних спроб. Foreground Service вимагає постійного повідомлення в статус-барі.
Обидві мобільні платформи постійно посилюють правила фонової роботи. На iOS кожне нове покоління ОС скорочує час роботи в фоновому режимі та додає нові обмеження. На Android Google вводить все суворіші режими енергозбереження (Doze, App Standby). Розробник повинен бути в курсі актуальних обмежень, щоб додаток не був завершений системою передчасно.
На iOS, починаючи з iOS 13, система відключає фонові завдання для додатків, які зловживають фоновим часом. Кожен додаток отримує певні ліміти на основі поведінки користувача. BGTaskScheduler планує виконання на оптимальний час — наприклад, коли пристрій підключений до Wi-Fi та заряджається. Додатки, які коректно використовують BGTaskScheduler, отримують більше фонового часу.
На Android починаючи з Android 9 (API 28) фонова робота обмежена режимом Doze, який активується при бездіяльності пристрою. Додатки в Doze не можуть виконувати фонові завдання, мережа відключається, JobScheduler та WorkManager відкладають завдання до виходу з Doze. Foreground Service — єдиний спосіб обійти Doze, але зловживання призводить до блокування додатка користувачем та видалення дозволів.
| Обмеження | iOS | Android |
|---|---|---|
| Таймаут фонового завдання | ~30 секунд (beginBackgroundTask) | Кілька хвилин (JobScheduler) |
| Необмежений фон | Аудіо, VoIP, навігація, Bluetooth | Foreground Service + повідомлення |
| Енергозбереження | Low Power Mode — відключає фонові завдання | Doze, App Standby, Battery Optimization |
| Планування | BGTaskScheduler (iOS 13+) | WorkManager (Android Jetpack) |
| Після перезавантаження | Тільки push-повідомлення | WorkManager зберігає завдання |
| Макс. час виконання | ~30 хвилин (аудіо) | Необмежено (Foreground Service) |
Перше правило — мінімізуйте споживання ресурсів у фоновому режимі. Більшість фонових завдань можна відкласти на час, коли пристрій заряджається та підключений до Wi-Fi. Використовуйте BGTaskScheduler (iOS) та WorkManager (Android) для планування завдань з умовами. Не запускайте важкі обчислення у фоновому режимі — це розряджає батарею та призводить до троттлінгу CPU.
Друге правило — завжди вказуйте expirationHandler для beginBackgroundTask. Якщо додаток не завершить завдання у відведений час, система примусово переведе його в Suspended або завершить. ExpirationHandler — останній шанс зберегти дані та коректно завершити роботу. На Android використовуйте setForegroundAsync в WorkManager для перетворення звичайного завдання на передній план, якщо потрібно більше часу.
Третє правило — перевіряйте обмеження фонової роботи перед запуском. На iOS використовуйте UIApplication.shared.backgroundTimeRemaining для перевірки часу, що залишився. На Android перевірте ActivityManager.isBackgroundRestricted() — якщо true, додаток не зможе запускати фонові завдання, і потрібно запропонувати користувачу зняти обмеження в налаштуваннях. Це особливо важливо для додатків з критичними фоновими функціями — будильники, календарі, синхронізація.
Четверте правило — тестуйте фонові завдання на реальному пристрої. Симулятор та емулятор не відтворюють реальні обмеження фонової роботи. На iOS використовуйте Debug → Simulate Background Fetch в Xcode. На Android — adb shell am broadcast -a android.intent.action.ACTION_BOOT_COMPLETED для тестування WorkManager після перезавантаження. Реальні тести на пристрої з низьким зарядом батареї виявляють більшість проблем фонової роботи.
import UIKit
final class BackgroundTaskManager {
static let shared = BackgroundTaskManager()
private var tasks: [String: UIBackgroundTaskIdentifier] = [:]
func startTask(name: String, expiration: @escaping () -> Void) {
let remaining = UIApplication.shared.backgroundTimeRemaining
print("Залишилося часу у фоновому режимі: \(remaining) сек")
let task = UIApplication.shared.beginBackgroundTask { [weak self] in
print("Час для завдання вичерпано: \(name)")
expiration()
self?.endTask(name: name)
}
tasks[name] = task
}
func endTask(name: String) {
guard let task = tasks.removeValue(forKey: name),
task != .invalid
else { return }
UIApplication.shared.endBackgroundTask(task)
}
}У коді показано менеджер фонових завдань, який відстежує час, що залишився, та управляє ідентифікаторами. backgroundTimeRemaining повертає кількість секунд до примусового завершення — якщо значення нескінченне, додаток працює без обмеження (аудіо, навігація). Менеджер дозволяє запускати кілька фонових завдань з різними іменами та коректно завершувати кожне. Такий підхід запобігає витоку фонових завдань і гарантує, що система не завершить додаток через незакриті завдання.
Часті запитання
Так, для обмеженої кількості категорій: аудіо (AVAudioSession категорія .playback), VoIP (PushKit), навігація (CLLocationManager з allowsBackgroundLocationUpdates), Bluetooth (central background mode), фонове оновлення (BGTaskScheduler). Для всіх інших — максимум 30 секунд. В iOS 16+ Apple посилила вимоги навіть для дозволених категорій.
beginBackgroundTask — синхронний API для продовження життя додатка на ~30 секунд після переходу в фон. Викликається в applicationDidEnterBackground. BGTaskScheduler — асинхронний API для планування завдань у майбутньому через системні тригери (час, місцезнаходження, оновлення контенту). BGTaskScheduler — сучасний підхід, рекомендований Apple для iOS 13+.
Починаючи з Android 8 (API 26), Background Service завершується через кілька хвилин після переходу додатка в фон. Рішення: використовуйте Foreground Service з повідомленням для довгих операцій або WorkManager для відкладених завдань. Перевірте Battery Optimization для вашого додатка в налаштуваннях — якщо воно оптимізоване, система може відкласти або скасувати фонові завдання.
Натисніть Cmd+Shift+H для переходу на головний екран. В Xcode використовуйте Debug → Simulate Background Fetch. Для перевірки beginBackgroundTask відкрийте консоль (Shift+Cmd+C) та викличте e UIApplication.shared.backgroundTimeRemaining. В Xcode 15+ доступний сценарій Background Execution у вкладці Diagnostics симулятора.
Process Death — завершення процесу Android системою при нестачі ресурсів або при бездіяльності у фоновому режимі. На відміну від iOS, Android не має Suspended — процес або живий (може бути в фоновому режимі), або мертвий (Not Running). Process Death — нормальна поведінка ОС, і додаток повинен коректно відновлювати стан після нього через SavedStateHandle, onSaveInstanceState або DataStore.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.