Background Task — 是iOS的一种机制,允许应用在切换到后台模式后完成已启动的操作。系统分配有限的时间(最多30秒)来执行任务,之后应用将被强制暂停。根据Apple,2026,使用expiration handler是正确完成后台任务的强制性要求。
要点
Background Task 是iOS的一种软件机制,允许应用在切换到后台模式后延长执行时间。当用户最小化应用时,系统调用 UIApplicationDelegate.applicationDidEnterBackground。如果此时正在执行关键操作(保存数据、发送请求),应用可以通过 beginBackgroundTask 请求额外时间。
如果不使用 Background Task,应用在进入后台3-5秒后会被强制暂停。所有未完成的操作都会被中断:数据无法保存,网络请求中断,状态丢失。Background Task 给应用最多30秒来正确完成这些操作。
beginBackgroundTask 出现在iOS 4.0中——这是第一个支持多任务处理的版本。在iOS 4之前,按下Home键时应用会完全关闭。从iOS 7开始,出现了Background Fetch和URLSession background configuration。从iOS 13开始,Apple建议新项目使用BGTaskScheduler,但beginBackgroundTask对于短期操作仍然适用。
beginBackgroundTask — 是UIApplication的一个方法,用于注册任务并返回唯一的UIBackgroundTaskIdentifier标识符。系统延长后台执行计时器。时间到期时,系统调用expiration handler,之后应用必须调用 endBackgroundTask(identifier:) 以正确完成。
第1步 — 应用收到 didEnterBackground 通知。第2步 — 调用 beginBackgroundTask(expirationHandler:)。第3步 — 执行关键操作(保存、网络请求)。第4步 — 完成后调用 endBackgroundTask(identifier:)。如果操作未在30秒内完成 — 系统调用expiration handler,应用必须立即结束任务。
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
}
backgroundTimeRemaining 属性返回后台任务强制结束前剩余的秒数。该值实时递减。如果应用不在后台 — 返回 DBL_MAX。使用此属性调整行为:当剩余不足5秒时,中断长时间操作并保存进度。
应用可以同时注册多个 Background Task。每个任务获得自己的标识符。总执行时间累加——如果注册了3个任务,应用可以获得最多90秒。但是,如果资源耗尽或超过电池限制,系统可能会提前结束应用。
Expiration handler 是系统在后台任务分配的时间到期时调用的代码块。拥有expiration handler是Apple的强制性要求。如果没有它,应用可能会被系统强制终止,数据可能会丢失。
Expiration handler 应执行最少量的操作以保存应用状态:将当前数据保存到持久存储中,使用相应的标识符调用 endBackgroundTask,并将标识符设置为 .invalid。在expiration handler内禁止启动新的长时间操作——执行时间限制为1-2秒。
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内部不调用 endBackgroundTask。在这种情况下,系统继续认为任务处于活动状态,应用不会进入Suspended状态,电池会耗尽。第二个错误是在expiration handler内部启动长时间操作。系统可能会在操作完成前终止应用,数据将丢失。
在expiration handler中,执行时间非常短暂——通常 不到1秒。因此,在handler内部不能执行:复杂计算、网络请求、向CoreData写入大量数据。只能进行原子操作:在UserDefaults中写入一个键、设置状态标志、调用endBackgroundTask。
我们来看一下在应用进入后台时保存文档的 Background Task 的完整实现。示例包括任务注册、执行带剩余时间检查的操作以及通过endBackgroundTask正确完成。
进入后台时,应用开始保存大型文档。Background Task 提供最多30秒的操作时间。如果时间到期——expiration handler保存中间结果。保存完成后,调用endBackgroundTask以释放资源。
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秒——将发送推迟到应用下次唤醒。
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 是一个强大的机制,但不正确使用会导致电池和性能问题。遵循Apple的建议和社区验证的实践有助于避免常见错误,确保应用稳定运行。
尽快完成Background Task。每秒钟的后台执行都会消耗电池电量。最佳时间 — 少于5秒。如果操作时间更长——考虑使用BGTaskScheduler或Background Modes。不要在无实际需要时通过beginBackgroundTask调用人为延长任务。
将 UIBackgroundTaskIdentifier 存储为类或结构的属性。切勿将其作为全局变量传递——这会在多个任务时导致冲突。在调用endBackgroundTask后将标识符设置为 .invalid,以防止重复完成。
在 Xcode Simulator 中通过Home键(Command + Shift + H)将应用切换到后台。使用Debug — Simulate Background Fetch测试后台唤醒。要模拟时间到期,设置环境变量 BACKGROUND_TASK_SIMULATE_EXPIRATION = YES — expiration handler将在5秒后被调用。
// 开始操作前检查剩余时间
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]或单独存储标识符。循环引用会导致内存泄漏——对象在应用结束前不会被释放。
随着iOS 13的发布,Apple推出了 BGTaskScheduler — 旧版Background Task API的现代替代品。两种机制解决相似的任务,但BGTaskScheduler提供更多的控制和灵活性。理解差异有助于为特定场景选择合适的工具。
| 特性 | Background Task | BGTaskScheduler |
|---|---|---|
| iOS版本 | 4.0+ | 13.0+ |
| 最大时间 | 30秒 | 30秒 / 10分钟(processing) |
| 启动方式 | didEnterBackground | 调度 + 系统 |
| 执行保证 | 仅在进入后台时 | 系统方便的任何时间 |
| Expiration handler | 是,必需的 | 是,通过task.expirationHandler |
| 网络 | 启动时需要 | 通过requiresNetworkConnectivity可用 |
| 充电 | 不需要 | processing可选 |
beginBackgroundTask 适用于需要在进入后台时立即执行的短期操作:保存状态、完成网络请求、缓存数据。API简单,不需要配置Info.plist或注册标识符。非常适合耗时少于10秒的操作。
BGTaskScheduler 适用于可以在任何方便时间执行的任务:定期同步、清理缓存、更新小部件。系统会根据用户行为和电池状态自行选择最佳时间。对于长时间操作(最多10分钟),使用BGProcessingTask。
两种API可以在一个应用中使用。Background Task — 用于进入后台时的即时操作(保存草稿)。BGTaskScheduler — 用于计划更新(每6小时同步一次)。职责分离可以确保数据正确保存和节能的后台服务。
常见问题
是的,beginBackgroundTask 可以在应用运行的任何时刻调用。但是,系统的时间分配只有在进入后台后才会开始。如果应用在前台,调用beginBackgroundTask没有效果——backgroundTimeRemaining返回DBL_MAX,任务将在进入后台时被激活。
如果不调用 endBackgroundTask,系统会继续认为应用在后台活跃。30秒后,expiration handler会被调用,但如果其中也没有endBackgroundTask——应用会留在内存中,消耗电池。在iOS 13+中,系统会在3分钟后强制终止此类应用。
标准时间是30秒。只能通过 Background Modes 延长:Audio(音频播放)、Location(地理定位)、Bluetooth(BLE)。或者通过BGProcessingTask(iOS 13+)——在充电和Wi-Fi条件下最多10分钟。beginBackgroundTask本身不提供增加限制的方法。
是的,iPadOS 完全支持beginBackgroundTask,限制与iOS相同。在带有Stage Manager的iPad上,应用可以在内存中停留更长时间——由于RAM更大,系统暂停应用的频率更低。但是30秒的后台任务限制仍然存在。
将设备连接到Xcode,运行应用,将其最小化——控制台将显示系统日志。使用 sysdiagnose 收集详细日志:从设备强制调用sysdiagnose(Volume Up + Down + Power)。在Xcode Debug Navigator中跟踪后台任务的活动。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。