You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DispatchQueue异步任务前后台存活问题及Firebase Token更新最佳实践

问题分析与解决方案

普通的DispatchQueue.global(qos: .background).async或主队列异步任务,在App进入后台后会被系统快速挂起,被终止后更是直接停止,根本没法保证执行。要实现后台持久执行,必须依赖iOS官方提供的后台机制,而非自行用GCD队列处理。

一、短时间后台操作:用后台任务断言

如果Token更新是几秒内能完成的短任务,可以通过UIApplication.shared.beginBackgroundTask申请额外后台执行时间:

func updateFirebaseToken() {
    var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
    
    // 申请后台任务
    backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "FirebaseTokenUpdate") { [weak self] in
        // 超时未完成,主动结束任务避免被系统杀死
        UIApplication.shared.endBackgroundTask(backgroundTaskID)
        backgroundTaskID = .invalid
    }
    
    DispatchQueue.global(qos: .background).async { [weak self] in
        defer {
            // 任务完成后必须结束后台任务
            if backgroundTaskID != .invalid {
                UIApplication.shared.endBackgroundTask(backgroundTaskID)
                backgroundTaskID = .invalid
            }
        }
        
        // 执行Firebase Token更新逻辑
        if let token = Messaging.messaging().fcmToken {
            // 上传Token到你的服务器
            // ...
        }
    }
}

注意:这种方式最多能拿到约30秒的后台执行时间,超时系统会强制终止任务。

二、周期性后台更新:用后台刷新任务

如果需要定期在后台更新Token(比如Token过期前),可以开启后台刷新权限:

  1. 在Xcode的Signing & Capabilities中添加Background Modes,勾选Background fetch
  2. 代码配置刷新间隔:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 设置最小刷新间隔,由系统决定实际触发时机
    UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
    return true
}
  1. 实现后台刷新回调:
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    // 检查Token是否需要更新
    if shouldUpdateFirebaseToken() {
        updateFirebaseToken()
        completionHandler(.newData)
    } else {
        completionHandler(.noData)
    }
}

注意:系统会根据用户使用习惯、设备电量等因素决定是否触发刷新,无法精确控制时间。

三、最优方案:利用Firebase原生回调处理Token更新

Firebase本身提供了Token变化的回调,不需要手动轮询或后台执行:

func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
    guard let token = fcmToken else { return }
    // 直接上传Token到服务器
    // 这个回调会在Token生成、更新时自动触发,即使App在后台(只要有网络)
}

当App被终止后,如果Token发生变化,Firebase会在下次App启动时触发该回调,所以启动时也需要检查一次:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    if let token = Messaging.messaging().fcmToken {
        uploadTokenToServer(token)
    }
    return true
}

后台运行最佳实践

  • 优先用系统原生后台机制,别自己用GCD队列模拟后台执行,系统会直接挂起这类任务
  • 后台任务要尽可能轻量化,避免长时间占用资源,否则会被系统标记为不良行为,后续后台权限会被限制
  • 对于Token这类无需频繁更新的内容,依赖Firebase原生回调而非周期性后台任务,减少后台唤醒次数
  • 务必处理后台任务的结束逻辑(比如endBackgroundTask),否则系统会认为App滥用后台权限,直接杀死进程
  • 若需长时间后台运行(如导航、音频类App),要申请对应后台模式权限,但Token更新不需要这类权限,别乱申请

内容的提问来源于stack exchange,提问作者Aswin Rathees

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 04:10:08