Flutter中Isolate调用OneSignal报错:ServicesBinding未初始化
我来帮你分析这个问题,并且给出可行的解决方案:
问题根源
你遇到的ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized错误,核心原因是Flutter插件(比如OneSignal)只能在主Isolate中运行。
你在main方法里调用WidgetsFlutterBinding.ensureInitialized()只能初始化主Isolate的绑定,而你通过compute创建的新Isolate是一个完全独立的执行环境——它没有初始化Flutter的服务绑定,也无法访问主Isolate的MethodChannel(插件和原生通信的核心通道)。OneSignal的所有方法(比如getPermissionSubscriptionState、getTags、sendTags)都依赖MethodChannel和原生端交互,所以在子Isolate中调用这些方法必然报错。
正确的解决方案
既然不能在子Isolate中调用插件方法,我们需要调整思路,在避免阻塞主Isolate的同时,在主Isolate中处理OneSignal的操作:
方案1:用异步方法非阻塞执行
OneSignal的API本身就是异步的,你不需要额外开Isolate,直接在主Isolate中异步执行即可,不会阻塞UI渲染。修改你的代码如下:
// 直接在主Isolate中执行的异步方法 Future<void> _handleOneSignalRegistration() async { String userId; while (userId == null) { var data = await OneSignal.shared.getPermissionSubscriptionState(); userId = data.subscriptionStatus.userId; if (userId != null) { print('*******************************************************************'); print('we got it inside while loop => $userId'); await CachingServices.saveStringField(key: 'one_signal_id', value: userId); print('************************************************************************'); print('===================================== fetching tags'); final Map<String,dynamic> myTags = await OneSignal.shared.getTags(); print(myTags); final Map<String,dynamic> tags = await HelperFucntions.getUserTags(); if(tags != null){ await OneSignal.shared.sendTags(tags); } } // 加个短延迟,避免循环太频繁占用资源 await Future.delayed(const Duration(milliseconds: 500)); } }
然后在initialize方法中直接调用这个异步方法,不需要用compute:
Future<void> initialize() async { if (_isInitialized) { return; } LocalNotificationService localNotificationService = LocalNotificationService.getInstance(); await localNotificationService.initialize(); OneSignal.shared.setLogLevel(OSLogLevel.verbose, OSLogLevel.none); OneSignal.shared.init( ONE_SIGNAL_ID, iOSSettings: {"OSiOSSettings.autoPrompt": false, "OSiOSSettings.inAppLaunchUrl": false}, ); OneSignal.shared.setInFocusDisplayType(OSNotificationDisplayType.none); await OneSignal.shared.promptUserForPushNotificationPermission(fallbackToSettings: true); // 直接异步执行,不会阻塞主Isolate的UI渲染 _handleOneSignalRegistration(); OneSignal.shared.setNotificationReceivedHandler( (OSNotification notification) { print(++_channelID); NotificationStateProvider notificationStateProvider = locator.get<NotificationStateProvider>(); var receivedNotification; try { receivedNotification = NotificationInstance( title: notification.payload.title, body: notification.payload.body, id: notification.payload.notificationId, sentAt: DateTime.fromMillisecondsSinceEpoch(notification.payload.rawPayload['google.sent_time']), ); notificationStateProvider.addToNotificationsList(receivedNotification); } catch (err) { throw err; } }, ); _isInitialized = true; }
方案2:拆分纯Dart代码到Isolate(如果有需要)
如果你的HelperFucntions.getUserTags()方法包含大量纯Dart计算逻辑(不依赖插件或原生API),可以把这部分单独放到Isolate中执行,拿到结果后再回到主Isolate调用OneSignal的sendTags:
// 纯Dart计算方法,不调用任何插件/原生API Future<Map<String, dynamic>> _computeUserTags() async { // 这里只放纯Dart逻辑,比如从本地缓存读取数据、计算标签等 return await HelperFucntions.getUserTags(); } // 主Isolate中执行的方法 Future<void> _handleOneSignalRegistration() async { String userId; while (userId == null) { var data = await OneSignal.shared.getPermissionSubscriptionState(); userId = data.subscriptionStatus.userId; if (userId != null) { print('*******************************************************************'); print('we got it inside while loop => $userId'); await CachingServices.saveStringField(key: 'one_signal_id', value: userId); print('************************************************************************'); print('===================================== fetching tags'); final Map<String,dynamic> myTags = await OneSignal.shared.getTags(); print(myTags); // 用compute执行纯Dart计算,不阻塞主Isolate final Map<String,dynamic> tags = await compute(_computeUserTags, null); if(tags != null){ await OneSignal.shared.sendTags(tags); } } await Future.delayed(const Duration(milliseconds: 500)); } }
关键知识点回顾
- Flutter的插件系统是和主Isolate绑定的,所有插件方法必须在主Isolate中调用
compute函数适合执行纯Dart计算任务,不能用来调用依赖原生的插件API- 异步方法本身不会阻塞主Isolate,因为Flutter的事件循环会处理异步任务,不会影响UI渲染
内容的提问来源于stack exchange,提问作者zero.one

