Background — stan cyklu życia aplikacji, w którym nadal działa, ale nie jest wyświetlany na ekranie. Wyjaśniamy podstawy pracy w tle na iOS i Android: ograniczenia, limity czasu, zadania w tle przez beginBackgroundTask, WorkManager i Service, a także najlepsze praktyki prawidłowego przetwarzania Background.
Najważniejsze
Background — stan aplikacji, w którym nadal istnieje w systemie operacyjnym, wykonuje kod i zużywa zasoby, ale nie jest wyświetlana na ekranie urządzenia. Użytkownik znajduje się na ekranie głównym, w innej aplikacji lub ekran urządzenia jest zablokowany. W iOS Background następuje po Inactive — łańcuch przejścia: Active → Inactive → Background. W Android onStop sygnalizuje przejście Activity do Background.
Obie platformy nakładają ścisłe ograniczenia na pracę w tle. iOS zapewnia ograniczone okno (zwykle 30 sekund) na wykonanie kodu po przejściu do Background, po czym aplikacja przechodzi w stan Suspended. Android jest bardziej elastyczny: Foreground Service z widocznym powiadomieniem może działać bez ograniczeń, ale zwykły Background Service jest ograniczony do kilku minut. Kluczowym zadaniem programisty jest poprawne zapisanie stanu i zaplanowanie kontynuacji pracy przez systemowe API zadań w tle.
System może zakończyć aplikację w tle w dowolnym momencie przy braku pamięci. W przypadku zakończenia wszystkie niezapisane dane są tracone. Dlatego krytycznie ważne jest zapisywanie stanu w applicationDidEnterBackground (iOS) lub onStop (Android). Po zakończeniu przy następnym uruchomieniu aplikacja startuje z Not Running z zimnym startem i przywraca zapisany stan.
Ważne jest rozróżnienie Background i Suspended. Background — aplikacja aktywnie wykonuje kod. Suspended — aplikacja znajduje się w pamięci, ale nie wykonuje kodu — jest zamrożona. W iOS aplikacja przechodzi z Background do Suspended po zakończeniu zadań w tle. W Android nie ma Suspended — proces albo istnieje (w tym w tle), albo jest zakończony (Not Running). Jednak Android może wstrzymać wykonywanie wątków przez LMK (Low Memory Killer).
| Charakterystyka | iOS Background | Android Background |
|---|---|---|
| Kod jest wykonywany | Tak, do 30 sekund | Tak, zależy od API |
| UI widoczny | Nie | Nie |
| Limit czasu domyślnie | ~30 sek (beginBackgroundTask) | Kilka minut (Service) |
| Nieograniczona praca | Tylko specjalne kategorie (audio, VoIP, nawigacja) | Foreground Service z powiadomieniem |
| Gwarancja wykonania | Nie — system może zakończyć w dowolnym momencie | WorkManager gwarantuje wykonanie |
| Wymagane uprawnienie | Tak — capabilities w Info.plist | Tak — FOREGROUND_SERVICE permission |
| Następny stan | Suspended → Not Running | Not Running (lub restart) |
W iOS Background jest obsługiwany przez metodę delegata applicationDidEnterBackground. W tej metodzie programista powinien zapisać stan użytkownika, zwolnić zasoby i zakończyć zadania w tle. Do wykonywania kodu po przejściu do Background używany jest beginBackgroundTask(expirationHandler:) — API, które żąda od systemu dodatkowego czasu (zwykle 30 sekund). Jeśli zadanie nie zakończy się w tym czasie, wywoływany jest expirationHandler, a aplikacja jest przymusowo przełączana w stan Suspended.
Od iOS 13 Apple wprowadziło BGTaskScheduler — nowoczesne API do planowania zadań w tle. W przeciwieństwie do beginBackgroundTask, który daje czas tylko na zakończenie po przejściu w tło, BGTaskScheduler pozwala zaplanować wykonanie zadań w przyszłości — na przykład aktualizację treści raz na godzinę lub wysyłkę analityki w nocy. BGTaskScheduler to zalecane podejście dla nowych projektów, ponieważ jest bardziej wydajne pod względem baterii.
import UIKit
import BackgroundTasks
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
// Aplikacja przeszła w tło — rozpoczynamy zadanie w tle
func applicationDidEnterBackground(_ application: UIApplication) {
saveAppState()
startBackgroundTask()
}
private func startBackgroundTask() {
backgroundTaskID = UIApplication.shared.beginBackgroundTask { [weak self] in
// Czas minął — wymuszamy zakończenie
self?.endBackgroundTask()
}
// Symulujemy pracę w tle (zapisywanie danych na serwer)
DispatchQueue.global().async { [weak self] in
uploadAnalyticsData()
self?.endBackgroundTask()
}
}
private func endBackgroundTask() {
guard backgroundTaskID != .invalid else { return }
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
// Rejestracja 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)
}
}
}Kod pokazuje pełną obsługę Background na iOS. applicationDidEnterBackground uruchamia zadanie w tle przez beginBackgroundTask z limitem czasu i expirationHandler. Równolegle rejestrowany jest BGTaskScheduler do okresowej aktualizacji treści. beginBackgroundTask jest używany do natychmiastowego zakończenia pracy, BGTaskScheduler — do długoterminowego planowania. Oba API wymagają prawidłowego zarządzania identyfikatorami zadań.
W Android Background jest zarządzany przez kilka API. Tradycyjny Service pozwala wykonywać kod w tle, ale od Android 8+ (API 26) Background Service jest ograniczony: system kończy go po kilku minutach od przejścia aplikacji w tło. Foreground Service ze stałym powiadomieniem może działać bez ograniczeń. WorkManager to zalecane rozwiązanie dla zadań w tle z gwarancją wykonania nawet po ponownym uruchomieniu urządzenia.
Android, w przeciwieństwie do iOS, obsługuje długożyciowe procesy w tle. Foreground Service jest używany do zadań, które użytkownik powinien widzieć — odtwarzanie muzyki, nawigacja, nagrywanie treningu. JobScheduler i WorkManager — do zadań, które mogą być odroczone: synchronizacja danych, wysyłka logów, aktualizacja cache. Kluczowa różnica: Android pozwala planować zadania z warunkami — Wi-Fi, ładowanie, nieaktywność urządzenia, co oszczędza baterię i transfer danych.
import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.work.*
// 1. Foreground Service dla długiej pracy w tle
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 {
// Synchronizacja danych z serwerem
syncDataToServer()
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
}.start()
}
override fun onBind(intent: Intent?): IBinder? = null
}
// 2. WorkManager dla odroczonych zadań w tle
class DataSyncWorker(
private val context: Context,
private val params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
// Wysyłka analityki na serwer
uploadAnalytics()
Result.success()
} catch (e: Exception) {
if (runAttemptCount < 3) Result.retry() else Result.failure()
}
}
}
// Planowanie zadania 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)
}Kod pokazuje dwa podejścia do pracy w tle na Android. SyncService — Foreground Service z powiadomieniem do natychmiastowej i długiej pracy w tle. DataSyncWorker — WorkManager dla zadań odroczonych z warunkami (Wi-Fi, ładowanie). WorkManager gwarantuje wykonanie nawet po ponownym uruchomieniu urządzenia i obsługuje exponential backoff dla ponownych prób. Foreground Service wymaga stałego powiadomienia w pasku statusu.
Obie platformy mobilne stale zaostrzają zasady pracy w tle. W iOS każde nowe pokolenie systemu operacyjnego skraca czas pracy w tle i dodaje nowe ograniczenia. W Android Google wprowadza coraz bardziej rygorystyczne tryby oszczędzania energii (Doze, App Standby). Programista musi być na bieżąco z aktualnymi ograniczeniami, aby aplikacja nie została przedwcześnie zakończona przez system.
W iOS począwszy od iOS 13, system wyłącza zadania w tle dla aplikacji, które nadużywają czasu w tle. Każda aplikacja otrzymuje określone limity na podstawie zachowania użytkownika. BGTaskScheduler planuje wykonanie na optymalny czas — na przykład gdy urządzenie jest podłączone do Wi-Fi i się ładuje. Aplikacje, które prawidłowo używają BGTaskScheduler, otrzymują więcej czasu w tle.
W Android począwszy od Android 9 (API 28) praca w tle jest ograniczona trybem Doze, który aktywuje się przy bezczynności urządzenia. Aplikacje w Doze nie mogą wykonywać zadań w tle, sieć jest wyłączona, JobScheduler i WorkManager odkładają zadania do wyjścia z Doze. Foreground Service to jedyny sposób na obejście Doze, ale nadużywanie prowadzi do zablokowania aplikacji przez użytkownika i usunięcia uprawnień.
| Ograniczenie | iOS | Android |
|---|---|---|
| Limit czasu zadania w tle | ~30 sekund (beginBackgroundTask) | Kilka minut (JobScheduler) |
| Nieograniczone tło | Audio, VoIP, nawigacja, Bluetooth | Foreground Service + powiadomienie |
| Oszczędzanie energii | Low Power Mode — wyłącza zadania w tle | Doze, App Standby, Battery Optimization |
| Planowanie | BGTaskScheduler (iOS 13+) | WorkManager (Android Jetpack) |
| Po ponownym uruchomieniu | Tylko push powiadomienie | WorkManager zachowuje zadania |
| Maks. czas wykonania | ~30 minut (audio) | Bez ograniczeń (Foreground Service) |
Pierwsza zasada — minimalizuj zużycie zasobów w tle. Większość zadań w tle można odroczyć na czas, gdy urządzenie się ładuje i jest podłączone do Wi-Fi. Używaj BGTaskScheduler (iOS) i WorkManager (Android) do planowania zadań z warunkami. Nie uruchamiaj ciężkich obliczeń w tle — to rozładowuje baterię i prowadzi do throttlingu CPU.
Druga zasada — zawsze określaj expirationHandler dla beginBackgroundTask. Jeśli aplikacja nie zakończy zadania w wyznaczonym czasie, system przymusowo przełączy ją w Suspended lub zakończy. ExpirationHandler to ostatnia szansa na zapisanie danych i poprawne zakończenie pracy. W Android używaj setForegroundAsync w WorkManager do przekształcenia zwykłego zadania w foreground, jeśli wymagany jest dłuższy czas.
Trzecia zasada — sprawdzaj ograniczenia pracy w tle przed uruchomieniem. W iOS używaj UIApplication.shared.backgroundTimeRemaining do sprawdzenia pozostałego czasu. W Android sprawdź ActivityManager.isBackgroundRestricted() — jeśli true, aplikacja nie będzie mogła uruchamiać zadań w tle i należy zaproponować użytkownikowi usunięcie ograniczeń w ustawieniach. Jest to szczególnie ważne dla aplikacji z krytycznymi funkcjami w tle — budziki, kalendarze, synchronizacja.
Czwarta zasada — testuj zadania w tle na rzeczywistym urządzeniu. Symulator i emulator nie odtwarzają rzeczywistych ograniczeń pracy w tle. W iOS używaj Debug → Simulate Background Fetch w Xcode. W Android — adb shell am broadcast -a android.intent.action.ACTION_BOOT_COMPLETED do testowania WorkManager po ponownym uruchomieniu. Rzeczywiste testy na urządzeniu z niskim poziomem baterii ujawniają większość problemów pracy w tle.
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("Pozostało czasu w tle: \(remaining) sek")
let task = UIApplication.shared.beginBackgroundTask { [weak self] in
print("Czas zakończył się dla zadania: \(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)
}
}Kod pokazuje menedżera zadań w tle, który śledzi pozostały czas i zarządza identyfikatorami. backgroundTimeRemaining zwraca liczbę sekund do przymusowego zakończenia — jeśli wartość jest nieskończona, aplikacja działa bez ograniczenia (audio, nawigacja). Menedżer pozwala uruchamiać wiele zadań w tle z różnymi nazwami i poprawnie zakończyć każde. Takie podejście zapobiega wyciekowi zadań w tle i gwarantuje, że system nie zakończy aplikacji z powodu niezamkniętych zadań.
Często zadawane pytania
Tak, dla ograniczonej liczby kategorii: audio (AVAudioSession kategoria .playback), VoIP (PushKit), nawigacja (CLLocationManager z allowsBackgroundLocationUpdates), Bluetooth (central background mode), aktualizacja w tle (BGTaskScheduler). Dla wszystkich pozostałych — maksymalnie 30 sekund. W iOS 16+ Apple zaostrzyła wymagania nawet dla dozwolonych kategorii.
beginBackgroundTask — synchroniczne API do przedłużenia życia aplikacji na ~30 sekund po przejściu w tło. Wywoływane w applicationDidEnterBackground. BGTaskScheduler — asynchroniczne API do planowania zadań w przyszłości przez wyzwalacze systemowe (czas, lokalizacja, aktualizacja treści). BGTaskScheduler to nowoczesne podejście zalecane przez Apple dla iOS 13+.
Od Android 8 (API 26), Background Service jest kończony po kilku minutach od przejścia aplikacji w tło. Rozwiązanie: użyj Foreground Service z powiadomieniem dla długich operacji lub WorkManager dla zadań odroczonych. Sprawdź Battery Optimization dla swojej aplikacji w ustawieniach — jeśli jest zoptymalizowana, system może odroczyć lub anulować zadania w tle.
Naciśnij Cmd+Shift+H aby przejść do ekranu głównego. W Xcode użyj Debug → Simulate Background Fetch. Aby sprawdzić beginBackgroundTask otwórz konsolę (Shift+Cmd+C) i wywołaj e UIApplication.shared.backgroundTimeRemaining. W Xcode 15+ dostępny jest scenariusz Background Execution w zakładce Diagnostics symulatora.
Process Death — zakończenie procesu Android przez system przy braku zasobów lub przy bezczynności w tle. W przeciwieństwie do iOS, Android nie ma Suspended — proces albo żyje (może być w tle), albo jest martwy (Not Running). Process Death to normalne zachowanie systemu operacyjnego, a aplikacja powinna poprawnie przywracać stan po nim przez SavedStateHandle, onSaveInstanceState lub DataStore.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również