一、iOS 后台运行机制与限制
iOS 后台模式概述
Swift
// Info.plist 中声明的后台模式
<key>UIBackgroundModes</key>
<array>
<string>audio</string> // 音频播放
<string>location</string> // 位置更新
<string>voip</string> // VoIP 通话
<string>fetch</string> // 后台获取
<string>processing</string> // 后台处理
<string>remote-notification</string> // 推送唤醒
</array>
1. Audio后台模式
需要在Xcode的"Capabilities"中开启Background Modes,并勾选"Audio, AirPlay, and Picture in Picture"。
Swift
import AVFoundation
class AudioBackgroundPlayer {
private let audioPlayer = AVPlayer()
private var isPlayingSilentAudio = false
func setupAudioSession() {
do {
// 配置音频会话支持后台播放
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playback,
mode: .default,
options: [.mixWithOthers])
try audioSession.setActive(true)
} catch {
print("音频会话设置失败: \(error)")
}
}
func startBackgroundAudio() {
guard !isPlayingSilentAudio else { return }
// 播放静音音频文件
if let url = Bundle.main.url(forResource: "silence", withExtension: "mp3") {
let playerItem = AVPlayerItem(url: url)
audioPlayer.replaceCurrentItem(with: playerItem)
audioPlayer.play()
// 设置循环播放
NotificationCenter.default.addObserver(
self,
selector: #selector(playerDidFinishPlaying),
name: .AVPlayerItemDidPlayToEndTime,
object: playerItem
)
isPlayingSilentAudio = true
}
}
@objc private func playerDidFinishPlaying() {
audioPlayer.seek(to: .zero)
audioPlayer.play()
}
func stopBackgroundAudio() {
audioPlayer.pause()
audioPlayer.replaceCurrentItem(with: nil)
isPlayingSilentAudio = false
}
}
// AppDelegate 中配置
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 配置音频会话支持后台播放
setupAudioSession()
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
//后台播放
}
}
二、官方推荐的保活方案
1. 后台任务 (Background Tasks)
利用beginBackgroundTask和endBackgroundTask来执行后台任务。后台任务将在应用程序进入后台时仍能保持有限的时间执行任务。
Swift
class BackgroundTaskManager {
private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
// 开始后台任务
func startBackgroundTask() {
// 申请后台执行时间(最多约30秒)
backgroundTaskID = UIApplication.shared.beginBackgroundTask(
withName: "com.app.backgroundTask"
) { [weak self] in
// 时间即将耗尽时执行清理
self?.endBackgroundTask()
}
// 在后台执行任务
DispatchQueue.global(qos: .background).async {
self.performBackgroundWork()
// 完成后必须结束任务
self.endBackgroundTask()
}
}
// 结束后台任务
private func endBackgroundTask() {
if backgroundTaskID != .invalid {
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
}
// 监控剩余时间
func monitorRemainingTime() {
let remainingTime = UIApplication.shared.backgroundTimeRemaining
print("剩余后台时间: \(remainingTime)秒")
if remainingTime < 10 {
// 时间不足,尽快保存状态
saveCriticalData()
}
}
}
2. 后台获取 (Background Fetch)
系统会间歇性地唤醒应用程序,以便它可以执行任务或获取数据。需要在Xcode的"Capabilities"中开启Background Modes,并勾选"Background fetch"。
Swift
// AppDelegate 中配置
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 设置后台获取最小间隔(系统决定实际间隔)
UIApplication.shared.setMinimumBackgroundFetchInterval(
UIApplication.backgroundFetchIntervalMinimum // 最小间隔
)
return true
}
// 后台获取回调
func application(_ application: UIApplication,
performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// 有限时间内执行数据获取(约30秒)
fetchNewData { newData in
if newData {
completionHandler(.newData)
} else {
completionHandler(.noData)
}
}
// 必须调用 completionHandler,否则应用可能被终止
}
private func fetchNewData(completion: @escaping (Bool) -> Void) {
// 模拟网络请求
DispatchQueue.global().async {
// 实际业务逻辑
let hasNewData = true
completion(hasNewData)
}
}
}
3. 静默推送 (Silent Push Notifications)
利用远程通知,在接收到通知时,系统会唤醒应用程序执行指定的任务。需要开启Remote notifications,在Application Capabilities中勾选"Remote notifications"。
Swift
// AppDelegate 中配置
class AppDelegate: UIResponder, UIApplicationDelegate {
// 处理静默推送
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
guard let aps = userInfo["aps"] as? [String: Any],
aps["content-available"] as? Int == 1 else {
completionHandler(.noData)
return
}
// 执行后台任务(约30秒)
processBackgroundTask { result in
completionHandler(result)
}
}
}
三. 长连接保活
1. WebSocket 保活策略
心跳机制:定期发送 PING/PONG 帧
重连机制:连接断开时自动重连
后台处理:合理利用后台任务维持连接
2. 组合技术方案
-
前台时:维持正常的 WebSocket 连接
-
进入后台时:
-
启动后台任务完成关键消息处理
-
关闭长连接以节省电量
-
切换到 APNs 推送接收重要消息
-
-
回到前台时:快速重建连接
3. 优化措施
-
使用
NSURLSession的 background session 进行网络传输 -
合理设置心跳间隔(一般 25-30 秒)
-
实现指数退避的重连策略
-
监控网络状态变化,及时调整策略
这种方案既保证了消息的及时性,又符合 iOS 的后台策略。
四、Swift Concurrency
1. Swift Concurrency 的核心优势
相比传统的 GCD,Swift Concurrency 提供了更高级的抽象:
结构化并发 :通过 Task、TaskGroup 等结构,自动管理任务生命周期,避免任务泄漏。
协作式调度:系统可以智能地挂起和恢复任务,在后台状态下更有效地管理资源。
Actor 模型 :通过 actor 确保状态安全,避免数据竞争,这对于后台任务的状态管理特别重要。
2. 与 BGTaskScheduler 的深度集成
在 iOS 13+ 中,我们可以将 Swift Concurrency 与 BGTaskScheduler 完美结合:
Swift
// 注册后台任务
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.app.refresh") { task in
Task {
// 使用 async/await 处理后台任务
await handleBackgroundWork(task: task)
}
}
这种组合的优势在于:
-
自动内存管理:Task 在完成后自动清理
-
更好的错误处理 :使用
try/catch和async/throws -
可取消性 :通过
Task.isCancelled响应系统中断
3. 实际应用模式
(1)周期性后台任务: 使用 Task.sleep 和循环创建周期性执行的任务
(2)数据同步保活: 利用 actor 确保数据同步的线程安全
(3)网络连接保活: 结合 Network 框架和 Swift Concurrency
4. 性能与资源管理
Swift Concurrency 在后台保活中的优势:
低开销:协程比线程更轻量,适合长时间运行的后台任务。
智能调度:系统可以根据设备状态(电池、温度)自动调整任务执行。
优先级管理 :通过 TaskPriority 明确指定后台任务的优先级。