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 — процесът или съществува (включително във фонов режим), или е прекратен (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) за планиране на задачи с условия. Не стартирайте тежки изчисления във фонов режим — това разрежда батерията и води до throttling на CPU.
Второ правило — винаги задавайте expirationHandler за beginBackgroundTask. Ако приложението не завърши задачата в определеното време, системата принудително ще го прехвърли в Suspended или ще го прекрати. ExpirationHandler е последният шанс да запазите данни и правилно да завършите работата. На Android използвайте setForegroundAsync в WorkManager за преобразуване на обикновена задача в foreground, ако е необходимо повече време.
Трето правило — проверявайте ограниченията за фонова работа преди стартиране. На 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 (централен фонов режим), фоново актуализиране (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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също