Flutter配置Workmanager iOS端报BGTaskSchedulerErrorDomain Code=3错误
Workmanager iOS端调度报错Code=3排查
问题现象
参照官方配置文档搭建Workmanager iOS端环境,实现指定时间触发本地通知的需求,Android端功能运行完全正常,iOS端运行时抛出如下错误:
Unhandled Exception: PlatformException(bgTaskSchedulingFailed(Error Domain=BGTaskSchedulerErrorDomain Code=3 "(null)") error, Scheduling the task using BGTaskScheduler has failed. This may be due to too many tasks being scheduled but not run. See the error details: Error Domain=BGTaskSchedulerErrorDomain Code=3 "(null)". , null, null) #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:647:7) #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:294:18) <asynchronous suspension> #2 Workmanager.registerOneOffTask (package:workmanager/src/workmanager.dart:187:7)
现有实现
Dart端代码
原本地通知回调与初始化逻辑:
// 本地通知触发函数 void callbackDispatcher() async { final now = DateTime.now(); Workmanager().executeTask((task, inputData) async { prefs = await SharedPreferences.getInstance(); if (prefs.getString('randomTime') != null) { final checkouttime = DateTime.parse(prefs.getString('randomTime')!); if (checkouttime.isBefore(now)) { NotificationApi.showNotification( body: "من فضلك قم بتاكيد تسجيلك", title: "My App", id: 1, payload: "payload", ); } } return Future.value(true); }); }
原主函数启动逻辑:
void main() async { WidgetsFlutterBinding.ensureInitialized(); NotificationApi.init(); await GetStorage.init(); final now = DateTime.now(); prefs = await SharedPreferences.getInstance(); //initialize workmanager await Workmanager().initialize(callbackDispatcher, isInDebugMode: false); //start workmanager await Workmanager().registerOneOffTask( "1", "simpleTask", ); }
iOS端配置
- info.plist配置:
<key>UIBackgroundModes</key> <array> <string>processing</string> </array> <key>BGTaskSchedulerPermittedIdentifiers</key> <array> <string>simpleTask</string> </array>
- AppDelegate.swift配置:
import UIKit import Flutter import workmanager @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) WorkmanagerPlugin.registerTask(withIdentifier: "simpleTask") return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
- project.pbxproj后台能力配置:
// !$*UTF8*$! { SystemCapabilities = { com.apple.BackgroundModes = { enabled = 1; }; }; ...
问题根因
- 重复调度冲突:App每次冷启动都会无条件调用
registerOneOffTask注册唯一ID为1的任务,没有配置重复任务处理策略,iOS端同ID任务未执行时重复注册会直接抛出Code=3错误(错误码对应调度不可用/任务冲突),多次冷启动后会出现任务队列堆积。 - 时间获取逻辑错误:
callbackDispatcher中DateTime.now()在回调初始化时就被赋值,任务实际触发时用的是App启动时的时间,不是任务执行的当前时间,会导致时间判断完全失效。 - 缺少必要任务参数:注册任务时没有配置
initialDelay(触发延迟)、existingWorkPolicy(重复任务处理策略),任务会被立刻调度,和预期的指定时间触发逻辑不符。 - 配置校验缺失:手动修改
project.pbxproj开启后台能力容易出现配置遗漏,Xcode没有正确识别后台权限时也会导致调度失败。 - 方案适配问题:iOS的
BGProcessingTask本身由系统统一调度,会结合设备电量、用户使用习惯、资源占用情况决定执行时机,不支持精准定时触发,最小调度间隔在15分钟以上,不适合做精准时间的本地通知触发。
修复方案
- 修正任务注册逻辑,避免重复调度:
注册前先判断任务是否已存在,或直接配置existingWorkPolicy: ExistingWorkPolicy.replace覆盖同ID旧任务,同时增加initialDelay参数配置任务延迟触发时间,和预期通知时间对齐。
参考代码:// 注册前先判断任务是否存在,避免重复注册 final bool isTaskRegistered = await Workmanager().isRegistered("1"); if (!isTaskRegistered) { final checkoutTime = DateTime.parse(prefs.getString('randomTime')!); await Workmanager().registerOneOffTask( "1", "simpleTask", existingWorkPolicy: ExistingWorkPolicy.replace, // 同ID任务直接覆盖 initialDelay: checkoutTime.difference(DateTime.now()), // 按目标时间设置延迟 constraints: Constraints( networkType: NetworkType.not_required, requiresBatteryNotLow: false, requiresCharging: false, requiresDeviceIdle: false, ), ); } - 修正回调内时间获取逻辑,把
now的获取移到任务执行回调内部,保证取到的是任务实际触发时的时间:void callbackDispatcher() { Workmanager().executeTask((task, inputData) async { final now = DateTime.now(); // 任务实际触发时再获取当前时间 final prefs = await SharedPreferences.getInstance(); if (prefs.getString('randomTime') != null) { final checkouttime = DateTime.parse(prefs.getString('randomTime')!); if (checkouttime.isBefore(now)) { NotificationApi.showNotification( body: "من فضلك قم بتاكيد تسجيلك", title: "My App", id: 1, payload: "payload", ); } } return Future.value(true); }); } - 重新校验iOS端配置:打开Xcode工程,在
Signing & Capabilities页确认已添加Background Modes能力,且勾选了Background processing选项,不要仅手动修改pbxproj文件,避免配置遗漏。 - 方案优化:如果需要精准时间触发本地通知,不要依赖Workmanager的后台调度,直接使用本地通知插件的定时调度能力,不需要后台任务权限,触发精度不受系统调度限制,更适合定时通知场景。
内容的提问来源于stack exchange,提问作者MikeNabil
相关产品推荐
相关产品推荐

