Background: fundamentals, how apps work in the background on iOS and Android

Author: IT Sectr Published: 2026-03-03 Reading time: 10 min

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 — the app is not visible to the user but can execute code for a limited time
  • iOS background task — beginBackgroundTask(expirationHandler:) gives up to 30 seconds to finish work
  • Android Service — Foreground Service with a notification for long-running background operations
  • WorkManager — recommended API for background tasks on Android with execution guarantee
  • Limitations — both platforms tighten background work rules to save battery

Background — fundamentals of background state

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.

Background vs Suspended

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).

CharacteristiciOS BackgroundAndroid Background
Code executesYes, up to 30 secondsYes, depends on API
UI visibleNoNo
Default timeout~30 sec (beginBackgroundTask)Several minutes (Service)
Unlimited workOnly special categories (audio, VoIP, navigation)Foreground Service with notification
Execution guaranteeNo — system may terminate anytimeWorkManager guarantees execution
Permission requiredYes — capabilities in Info.plistYes — FOREGROUND_SERVICE permission
Next stateSuspended → Not RunningNot Running (or restart)

Background on iOS: Swift, beginBackgroundTask and BGTaskScheduler

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.

swift
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.

Background on Android: Kotlin, Service, WorkManager

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.

kotlin
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.

Background work limitations on iOS and Android

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.

RestrictioniOSAndroid
Background task timeout~30 seconds (beginBackgroundTask)Several minutes (JobScheduler)
Unlimited backgroundAudio, VoIP, navigation, BluetoothForeground Service + notification
Power savingLow Power Mode — disables background tasksDoze, App Standby, Battery Optimization
SchedulingBGTaskScheduler (iOS 13+)WorkManager (Android Jetpack)
After rebootOnly push notificationWorkManager persists tasks
Max execution time~30 minutes (audio)Unlimited (Foreground Service)

Best practices for background work

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.

swift
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

Can an iOS app run in the background forever?

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.

How does beginBackgroundTask differ from BGTaskScheduler?

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+.

Why does Android kill my Background Service?

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.

How to test Background on the iOS simulator?

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.

What is process death on Android?

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

  • Background — the app is not visible on screen but executes code, unlike Suspended (frozen)
  • iOS — beginBackgroundTask (up to 30 sec) and BGTaskScheduler for scheduling future tasks
  • Android — Foreground Service for long-running operations, WorkManager for deferred tasks with guarantee
  • Limitations — both platforms tighten background work rules: Doze, Low Power Mode, App Standby
  • Saving — applicationDidEnterBackground and onStop are the last chance to save data before Suspended/Not Running
  • Scheduling — BGTaskScheduler and WorkManager work with conditions (Wi-Fi, charging, time)
  • Foreground Service — the only way for unlimited background work on both platforms

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.

Discuss the project

Read also