Background Task:关键概念、任务注册与后台执行

作者: IT Sectr 发布日期: 2026-03-27 阅读时间: 10 分钟

Background Task — 是iOS的一种机制,允许应用在切换到后台模式后完成已启动的操作。系统分配有限的时间(最多30秒)来执行任务,之后应用将被强制暂停。根据Apple,2026,使用expiration handler是正确完成后台任务的强制性要求。

要点

  • Background Task — iOS在应用进入后台后完成操作的机制。
  • beginBackgroundTask — UIApplication中用于注册后台任务的关键方法。
  • Expiration handler — 系统在分配时间到期时调用的代码块。
  • 30秒限制 — 标准执行时间,可通过Background Modes延长。
  • UIApplication.shared.endBackgroundTask — 正确完成任务所必需的调用。

iOS中的Background Task是什么?

Background Task 是iOS的一种软件机制,允许应用在切换到后台模式后延长执行时间。当用户最小化应用时,系统调用 UIApplicationDelegate.applicationDidEnterBackground。如果此时正在执行关键操作(保存数据、发送请求),应用可以通过 beginBackgroundTask 请求额外时间。

为什么需要Background Tasks

如果不使用 Background Task,应用在进入后台3-5秒后会被强制暂停。所有未完成的操作都会被中断:数据无法保存,网络请求中断,状态丢失。Background Task 给应用最多30秒来正确完成这些操作。

  • 保存状态 — 将数据写入CoreData、UserDefaults或文件系统。
  • 完成网络请求 — 等待服务器响应并处理结果。
  • 发送分析数据 — 在暂停前将事件传输到分析服务器。
  • 清理资源 — 关闭文件描述符,释放内存。
  • 缓存 — 写入中间结果以便下次启动时恢复。

历史与演进

beginBackgroundTask 出现在iOS 4.0中——这是第一个支持多任务处理的版本。在iOS 4之前,按下Home键时应用会完全关闭。从iOS 7开始,出现了Background Fetch和URLSession background configuration。从iOS 13开始,Apple建议新项目使用BGTaskScheduler,但beginBackgroundTask对于短期操作仍然适用。

beginBackgroundTask如何工作:生命周期

beginBackgroundTask — 是UIApplication的一个方法,用于注册任务并返回唯一的UIBackgroundTaskIdentifier标识符。系统延长后台执行计时器。时间到期时,系统调用expiration handler,之后应用必须调用 endBackgroundTask(identifier:) 以正确完成。

逐步执行过程

第1步 — 应用收到 didEnterBackground 通知。第2步 — 调用 beginBackgroundTask(expirationHandler:)。第3步 — 执行关键操作(保存、网络请求)。第4步 — 完成后调用 endBackgroundTask(identifier:)。如果操作未在30秒内完成 — 系统调用expiration handler,应用必须立即结束任务。

swift
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid

func startBackgroundTask() {
    backgroundTaskID = UIApplication.shared.beginBackgroundTask {
        // Expiration handler
        UIApplication.shared.endBackgroundTask(backgroundTaskID)
        backgroundTaskID = .invalid
    }
}

func finishBackgroundTask() {
    UIApplication.shared.endBackgroundTask(backgroundTaskID)
    backgroundTaskID = .invalid
}

UIApplication.shared.backgroundTimeRemaining

backgroundTimeRemaining 属性返回后台任务强制结束前剩余的秒数。该值实时递减。如果应用不在后台 — 返回 DBL_MAX。使用此属性调整行为:当剩余不足5秒时,中断长时间操作并保存进度。

多个任务

应用可以同时注册多个 Background Task。每个任务获得自己的标识符。总执行时间累加——如果注册了3个任务,应用可以获得最多90秒。但是,如果资源耗尽或超过电池限制,系统可能会提前结束应用。

Expiration handler:强制完成处理

Expiration handler 是系统在后台任务分配的时间到期时调用的代码块。拥有expiration handler是Apple的强制性要求。如果没有它,应用可能会被系统强制终止,数据可能会丢失。

Expiration handler中应该包含什么

Expiration handler 应执行最少量的操作以保存应用状态:将当前数据保存到持久存储中,使用相应的标识符调用 endBackgroundTask,并将标识符设置为 .invalid。在expiration handler内禁止启动新的长时间操作——执行时间限制为1-2秒。

swift
func handleExpiration() {
    // 保存执行进度
    saveProgressToUserDefaults()

    // 取消活动的网络请求
    currentTask?.cancel()

    // 结束Background Task
    UIApplication.shared.endBackgroundTask(backgroundTaskID)
    backgroundTaskID = .invalid
}

// 使用expiration handler注册
backgroundTaskID = UIApplication.shared.beginBackgroundTask(
    withName: "SaveDocument",
    expirationHandler: handleExpiration
)

使用expiration handler时常见的错误

最常见的错误是在expiration handler内部不调用 endBackgroundTask。在这种情况下,系统继续认为任务处于活动状态,应用不会进入Suspended状态,电池会耗尽。第二个错误是在expiration handler内部启动长时间操作。系统可能会在操作完成前终止应用,数据将丢失。

检查剩余时间

在expiration handler中,执行时间非常短暂——通常 不到1秒。因此,在handler内部不能执行:复杂计算、网络请求、向CoreData写入大量数据。只能进行原子操作:在UserDefaults中写入一个键、设置状态标志、调用endBackgroundTask。

Swift中的Background Task代码示例

我们来看一下在应用进入后台时保存文档的 Background Task 的完整实现。示例包括任务注册、执行带剩余时间检查的操作以及通过endBackgroundTask正确完成。

保存文档并显示进度

进入后台时,应用开始保存大型文档。Background Task 提供最多30秒的操作时间。如果时间到期——expiration handler保存中间结果。保存完成后,调用endBackgroundTask以释放资源。

swift
class DocumentManager {
    private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid

    func saveDocumentInBackground(document: Document) {
        backgroundTaskID = UIApplication.shared.beginBackgroundTask {
            self.saveProgress(document)
            self.endBackgroundTask()
        }

        DispatchQueue.global().async {
            document.save()
            self.endBackgroundTask()
        }
    }

    private func endBackgroundTask() {
        UIApplication.shared.endBackgroundTask(backgroundTaskID)
        backgroundTaskID = .invalid
    }
}

向服务器发送数据

后台的 网络请求 需要特别注意——expiration handler可能会在收到响应前中断请求。使用带dataTask的URLSession,并在发送前检查backgroundTimeRemaining。如果剩余不足5秒——将发送推迟到应用下次唤醒。

swift
func sendCriticalData(data: Data) {
    backgroundTaskID = UIApplication.shared.beginBackgroundTask {
        // 保存数据以便重新发送
        saveForRetry(data)
        self.endBackgroundTask()
    }

    let task = URLSession.shared.dataTask(with: request) { _, _, error in
        if error != nil {
            saveForRetry(data)
        }
        self.endBackgroundTask()
    }
    task.resume()
}

监控剩余时间

对于长时间操作,请监控 UIApplication.shared.backgroundTimeRemaining。如果值低于阈值(10秒),中断当前操作并开始保存中间结果。这允许在expiration handler被强制调用前正确完成工作。

使用Background Task的最佳实践

Background Task 是一个强大的机制,但不正确使用会导致电池和性能问题。遵循Apple的建议和社区验证的实践有助于避免常见错误,确保应用稳定运行。

最小化执行时间

尽快完成Background Task。每秒钟的后台执行都会消耗电池电量。最佳时间 — 少于5秒。如果操作时间更长——考虑使用BGTaskScheduler或Background Modes。不要在无实际需要时通过beginBackgroundTask调用人为延长任务。

正确使用标识符

UIBackgroundTaskIdentifier 存储为类或结构的属性。切勿将其作为全局变量传递——这会在多个任务时导致冲突。在调用endBackgroundTask后将标识符设置为 .invalid,以防止重复完成。

测试Background Task

Xcode Simulator 中通过Home键(Command + Shift + H)将应用切换到后台。使用Debug — Simulate Background Fetch测试后台唤醒。要模拟时间到期,设置环境变量 BACKGROUND_TASK_SIMULATE_EXPIRATION = YES — expiration handler将在5秒后被调用。

swift
// 开始操作前检查剩余时间
let remaining = UIApplication.shared.backgroundTimeRemaining
guard remaining > 10.0 else {
    // 时间不足——推迟任务
    scheduleForNextLaunch()
    return
}

// 带时间控制的操作执行
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
    saveProgress()
    self.endBackgroundTask()
}

避免循环引用

Expiration handler 捕获 self — 如果backgroundTaskID作为同一对象的属性存储,这可能会造成循环引用。在闭包中使用[weak self]或单独存储标识符。循环引用会导致内存泄漏——对象在应用结束前不会被释放。

Background Task与BGTaskScheduler:方法比较

随着iOS 13的发布,Apple推出了 BGTaskScheduler — 旧版Background Task API的现代替代品。两种机制解决相似的任务,但BGTaskScheduler提供更多的控制和灵活性。理解差异有助于为特定场景选择合适的工具。

特性Background TaskBGTaskScheduler
iOS版本4.0+13.0+
最大时间30秒30秒 / 10分钟(processing)
启动方式didEnterBackground调度 + 系统
执行保证仅在进入后台时系统方便的任何时间
Expiration handler是,必需的是,通过task.expirationHandler
网络启动时需要通过requiresNetworkConnectivity可用
充电不需要processing可选

何时使用Background Task

beginBackgroundTask 适用于需要在进入后台时立即执行的短期操作:保存状态、完成网络请求、缓存数据。API简单,不需要配置Info.plist或注册标识符。非常适合耗时少于10秒的操作。

何时使用BGTaskScheduler

BGTaskScheduler 适用于可以在任何方便时间执行的任务:定期同步、清理缓存、更新小部件。系统会根据用户行为和电池状态自行选择最佳时间。对于长时间操作(最多10分钟),使用BGProcessingTask。

共同使用

两种API可以在一个应用中使用。Background Task — 用于进入后台时的即时操作(保存草稿)。BGTaskScheduler — 用于计划更新(每6小时同步一次)。职责分离可以确保数据正确保存和节能的后台服务。

常见问题

可以在didEnterBackground之外调用beginBackgroundTask吗?

是的,beginBackgroundTask 可以在应用运行的任何时刻调用。但是,系统的时间分配只有在进入后台后才会开始。如果应用在前台,调用beginBackgroundTask没有效果——backgroundTimeRemaining返回DBL_MAX,任务将在进入后台时被激活。

如果不调用endBackgroundTask会发生什么?

如果不调用 endBackgroundTask,系统会继续认为应用在后台活跃。30秒后,expiration handler会被调用,但如果其中也没有endBackgroundTask——应用会留在内存中,消耗电池。在iOS 13+中,系统会在3分钟后强制终止此类应用。

如何将Background Task时间延长到30秒以上?

标准时间是30秒。只能通过 Background Modes 延长:Audio(音频播放)、Location(地理定位)、Bluetooth(BLE)。或者通过BGProcessingTask(iOS 13+)——在充电和Wi-Fi条件下最多10分钟。beginBackgroundTask本身不提供增加限制的方法。

Background Task在iPad上有效吗?

是的,iPadOS 完全支持beginBackgroundTask,限制与iOS相同。在带有Stage Manager的iPad上,应用可以在内存中停留更长时间——由于RAM更大,系统暂停应用的频率更低。但是30秒的后台任务限制仍然存在。

如何在真实设备上调试Background Task?

将设备连接到Xcode,运行应用,将其最小化——控制台将显示系统日志。使用 sysdiagnose 收集详细日志:从设备强制调用sysdiagnose(Volume Up + Down + Power)。在Xcode Debug Navigator中跟踪后台任务的活动。

总结

  • Background Task — iOS中进入后台后完成操作的机制,从iOS 4.0开始可用。
  • beginBackgroundTask — UIApplication中注册任务并返回标识符以完成该任务的方法。
  • Expiration handler — 系统在30秒限制到期时调用的必需代码块。
  • endBackgroundTask — 正确完成任务必需的调用;没有它,应用会留在内存中。
  • BGTaskScheduler — 现代替代方案(iOS 13+),支持调度、两种任务类型和执行条件。
  • backgroundTimeRemaining — 用于检查剩余时间和调整任务行为的属性。
  • 在新项目中,使用 Background Task 进行即时短期操作,使用 BGTaskScheduler 进行计划后台更新。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读