Background is a lifecycle state of an app where it continues running but is not displayed on screen. We explain the basics of background work on iOS and Android: limitations, timeouts, background tasks via beginBackgroundTask, WorkManager and Service, as well as best practices for correct Background handling.
Key Takeaways
Background is a state of an app where it continues to exist in the operating system, execute code and consume resources, but is not displayed on the device screen. The user is on the Home Screen, in another app, or the device screen is locked. On iOS, Background follows Inactive — the transition chain is: Active → Inactive → Background. On Android, onStop signals the transition of an Activity to Background.
Both platforms impose strict restrictions on background work. iOS provides a limited window (typically 30 seconds) to execute code after entering Background, after which the app transitions to Suspended. Android is more flexible: a Foreground Service with a visible notification can run indefinitely, but a regular Background Service is limited to a few minutes. The developer's key task is to properly save state and schedule continuation of work through system background task APIs.
The system may terminate a background app at any time when memory is low. Upon termination, all unsaved data is lost. Therefore it is critically important to save state in applicationDidEnterBackground (iOS) or onStop (Android). After termination, the next launch starts from Not Running with a cold start and restores the saved state.
It is important to distinguish Background from Suspended. Background — the app is actively executing code. Suspended — the app is in memory but not executing code — it is frozen. On iOS, the app transitions from Background to Suspended after completing background tasks. Android has no Suspended — the process either exists (including Background) or is terminated (Not Running). However, Android can pause thread execution via LMK (Low Memory Killer).
| Characteristic | iOS Background | Android Background |
|---|---|---|
| Code executes | Yes, up to 30 seconds | Yes, depends on API |
| UI visible | No | No |
| Default timeout | ~30 sec (beginBackgroundTask) | Several minutes (Service) |
| Unlimited work | Only special categories (audio, VoIP, navigation) | Foreground Service with notification |
| Execution guarantee | No — system may terminate anytime | WorkManager guarantees execution |
| Permission required | Yes — capabilities in Info.plist | Yes — FOREGROUND_SERVICE permission |
| Next state | Suspended → Not Running | Not Running (or restart) |
On iOS, Background is handled through the delegate method applicationDidEnterBackground. In this method, the developer should save user state, release resources, and complete background tasks. To execute code after entering Background, beginBackgroundTask(expirationHandler:) is used — an API that requests additional time from the system (usually 30 seconds). If the task does not complete within this time, the expirationHandler is called, and the app is forcefully transitioned to Suspended.
With iOS 13, Apple introduced BGTaskScheduler — a modern API for scheduling background tasks. Unlike beginBackgroundTask, which only gives time to finish after going into the background, BGTaskScheduler allows scheduling task execution in the future — for example, updating content once an hour or uploading analytics at night. BGTaskScheduler is the recommended approach for new projects, as it is more battery-efficient.
import UIKit
import BackgroundTasks
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
// App went to background — starting background task
func applicationDidEnterBackground(_ application: UIApplication) {
saveAppState()
startBackgroundTask()
}
private func startBackgroundTask() {
backgroundTaskID = UIApplication.shared.beginBackgroundTask { [weak self] in
// Time expired — force finishing
self?.endBackgroundTask()
}
// Simulating background work (saving data to server)
DispatchQueue.global().async { [weak self] in
uploadAnalyticsData()
self?.endBackgroundTask()
}
}
private func endBackgroundTask() {
guard backgroundTaskID != .invalid else { return }
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
// BGTaskScheduler registration
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)
}
}
}The code shows complete Background handling on iOS. applicationDidEnterBackground starts a background task via beginBackgroundTask with a timeout and expirationHandler. At the same time, BGTaskScheduler is registered for periodic content updates. beginBackgroundTask is used for immediate shutdown tasks, BGTaskScheduler is used for long-term planning. Both APIs require proper management of task identifiers.
On Android, Background is managed through several APIs. The traditional Service allows executing code in the background, but since Android 8+ (API 26), Background Service is limited: the system terminates it a few minutes after the app goes into the background. A Foreground Service with a persistent notification can run indefinitely. WorkManager is the recommended solution for background tasks with execution guarantee even after device reboot.
Android, unlike iOS, supports long-running background processes. Foreground Service is used for tasks the user should see — music playback, navigation, workout tracking. JobScheduler and WorkManager are used for tasks that can be deferred: data synchronization, log upload, cache update. The key difference: Android allows scheduling tasks with conditions — Wi-Fi, charging, device idle — which saves battery and traffic.
import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.work.*
// 1. Foreground Service for long-running background work
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 {
// Data synchronization with server
syncDataToServer()
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
}.start()
}
override fun onBind(intent: Intent?): IBinder? = null
}
// 2. WorkManager for deferred background tasks
class DataSyncWorker(
private val context: Context,
private val params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
// Uploading analytics to server
uploadAnalytics()
Result.success()
} catch (e: Exception) {
if (runAttemptCount < 3) Result.retry() else Result.failure()
}
}
}
// WorkManager task scheduling
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)
}The code shows two approaches to background work on Android. SyncService — a Foreground Service with a notification for immediate and long-running background work. DataSyncWorker — WorkManager for deferred tasks with conditions (Wi-Fi, charging). WorkManager guarantees execution even after device reboot and supports exponential backoff for retries. Foreground Service requires a persistent notification in the status bar.
Both mobile platforms are constantly tightening background work rules. On iOS, each new OS generation reduces background execution time and adds new restrictions. On Android, Google introduces increasingly stricter power saving modes (Doze, App Standby). Developers must stay up to date with current limitations to prevent the app from being prematurely terminated by the system.
On iOS, starting with iOS 13, the system disables background tasks for apps that abuse background time. Each app receives certain limits based on user behavior. BGTaskScheduler schedules execution at optimal times — for example, when the device is connected to Wi-Fi and charging. Apps that correctly use BGTaskScheduler get more background time.
On Android, starting with Android 9 (API 28), background work is restricted by Doze mode, which activates when the device is idle. Apps in Doze cannot perform background tasks, the network is disconnected, JobScheduler and WorkManager defer tasks until exiting Doze. Foreground Service is the only way to bypass Doze, but abuse leads to the app being blocked by the user and permissions being revoked.
| Restriction | iOS | Android |
|---|---|---|
| Background task timeout | ~30 seconds (beginBackgroundTask) | Several minutes (JobScheduler) |
| Unlimited background | Audio, VoIP, navigation, Bluetooth | Foreground Service + notification |
| Power saving | Low Power Mode — disables background tasks | Doze, App Standby, Battery Optimization |
| Scheduling | BGTaskScheduler (iOS 13+) | WorkManager (Android Jetpack) |
| After reboot | Only push notification | WorkManager persists tasks |
| Max execution time | ~30 minutes (audio) | Unlimited (Foreground Service) |
First rule — minimize resource consumption in the background. Most background tasks can be deferred to when the device is charging and connected to Wi-Fi. Use BGTaskScheduler (iOS) and WorkManager (Android) for scheduling tasks with conditions. Do not run heavy computations in the background — this drains the battery and leads to CPU throttling.
Second rule — always specify an expirationHandler for beginBackgroundTask. If the app does not complete the task within the allotted time, the system will forcefully transition it to Suspended or terminate it. The expirationHandler is the last chance to save data and properly finish work. On Android, use setForegroundAsync in WorkManager to convert a regular task to foreground if more time is needed.
Third rule — check background work restrictions before launching. On iOS, use UIApplication.shared.backgroundTimeRemaining to check remaining time. On Android, check ActivityManager.isBackgroundRestricted() — if true, the app cannot run background tasks, and you should suggest the user remove restrictions in settings. This is especially important for apps with critical background functions — alarms, calendars, synchronization.
Fourth rule — test background tasks on a real device. Simulators and emulators do not reproduce real background work restrictions. On iOS, use Debug → Simulate Background Fetch in Xcode. On Android, use adb shell am broadcast -a android.intent.action.ACTION_BOOT_COMPLETED for testing WorkManager after reboot. Real tests on a device with low battery reveal most background work issues.
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("Background time remaining: \(remaining) sec")
let task = UIApplication.shared.beginBackgroundTask { [weak self] in
print("Time expired for task: \(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)
}
}The code shows a background task manager that tracks remaining time and manages identifiers. backgroundTimeRemaining returns the number of seconds before forced termination — if the value is infinite, the app is running without restrictions (audio, navigation). The manager allows launching multiple background tasks with different names and properly completing each one. This approach prevents background task leaks and ensures the system does not terminate the app due to unclosed tasks.
Frequently Asked Questions
Yes, for a limited number of categories: audio (AVAudioSession category .playback), VoIP (PushKit), navigation (CLLocationManager with allowsBackgroundLocationUpdates), Bluetooth (central background mode), background refresh (BGTaskScheduler). For all others — a maximum of 30 seconds. In iOS 16+, Apple has tightened requirements even for permitted categories.
beginBackgroundTask is a synchronous API for extending the app's life by ~30 seconds after going into the background. It is called in applicationDidEnterBackground. BGTaskScheduler is an asynchronous API for scheduling tasks in the future via system triggers (time, location, content update). BGTaskScheduler is the modern approach, recommended by Apple for iOS 13+.
Starting with Android 8 (API 26), a Background Service is terminated a few minutes after the app goes into the background. Solution: use a Foreground Service with a notification for long-running operations or WorkManager for deferred tasks. Check Battery Optimization for your app in settings — if it is optimized, the system may defer or cancel background tasks.
Press Cmd+Shift+H to go to the Home Screen. In Xcode, use Debug → Simulate Background Fetch. To check beginBackgroundTask, open the console (Shift+Cmd+C) and call e UIApplication.shared.backgroundTimeRemaining. In Xcode 15+, a Background Execution scenario is available in the Diagnostics tab of the simulator.
Process Death is the termination of an Android process by the system when resources are low or when idle in the background. Unlike iOS, Android has no Suspended — the process is either alive (can be in the background) or dead (Not Running). Process Death is normal OS behavior, and the app must properly restore state after it via SavedStateHandle, onSaveInstanceState, or DataStore.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also