Flutter中Firebase Messaging订阅Topic偶现SERVICE_NOT_AVAILABLE错误求助
APP启动时尝试让用户订阅指定Topic以接收通知,但存在偶发性问题:有时订阅成功,有时抛出错误[firebase_messaging/unknown] java.io.IOException: SERVICE_NOT_AVAILABLE。
相关代码
@override void initState() { super.initState(); lifeCycleHandler(); WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { ... await ini(context); }); } Future<void> ini(BuildContext context) async { ... FirebaseMessaging.instance.subscribeToTopic(topic).whenComplete(() { debugPrint('successfully subscribed to $topic'); },).onError((error, stackTrace) { debugPrint('an error occurred while subscribing to topic $topic \n $stackTrace \n error $error'); },); }
错误日志
E/flutter (17003): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: [firebase_messaging/unknown] java.io.IOException: SERVICE_NOT_AVAILABLE
E/flutter (17003): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:653:7)
E/flutter (17003): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:315:18)
E/flutter (17003):
E/flutter (17003): #2 MethodChannel.invokeMapMethod (package:flutter/src/services/platform_channel.dart:518:43)
E/flutter (17003):
E/flutter (17003): #3 MethodChannelFirebaseMessaging.deleteToken (package:firebase_messaging_platform_interface/src/method_channel/method_channel_messaging.dart:192:7)
E/flutter (17003):
E/flutter (17003): #4 MyMenuState.initToken (package:buffet/Screens/menu.dart:3250:5)
E/flutter (17003):
E/flutter (17003): #5 MyMenuState.ini (package:buffet/Screens/menu.dart:1682:5)
E/flutter (17003):
E/flutter (17003): #6 MyMenuState.initState. (package:buffet/Screens/menu.dart:132:7)
E/flutter (17003):
解决方案
1. 等Firebase初始化完成再订阅
当前代码在页面帧回调里执行订阅,但Firebase Messaging可能还没完全初始化。先确保Firebase核心加载完成再操作:
Future<void> ini(BuildContext context) async { // 先等待Firebase初始化 await Firebase.initializeApp(); try { await FirebaseMessaging.instance.subscribeToTopic(topic); debugPrint('successfully subscribed to $topic'); } catch (error, stackTrace) { debugPrint('an error occurred while subscribing to topic $topic \n $stackTrace \n error $error'); } }
2. 加重试机制
SERVICE_NOT_AVAILABLE大多是临时网络波动或服务未就绪导致的,加指数退避重试能解决偶发问题:
Future<void> subscribeWithRetry(String topic, int retryCount) async { try { await FirebaseMessaging.instance.subscribeToTopic(topic); debugPrint('successfully subscribed to $topic'); } catch (e) { if (retryCount > 0 && e.toString().contains('SERVICE_NOT_AVAILABLE')) { // 指数退避,每次等待时间翻倍 await Future.delayed(Duration(seconds: (4 - retryCount) * 2)); await subscribeWithRetry(topic, retryCount - 1); } else { debugPrint('Failed to subscribe after retries: $e'); } } } // 在ini方法里调用 Future<void> ini(BuildContext context) async { await Firebase.initializeApp(); await subscribeWithRetry(topic, 3); // 最多重试3次 }
3. 提前检查网络状态
订阅前先确认设备有可用网络,避免无网导致的服务不可用错误:
import 'package:connectivity_plus/connectivity_plus.dart'; Future<void> ini(BuildContext context) async { await Firebase.initializeApp(); var connectivityResult = await (Connectivity().checkConnectivity()); if (connectivityResult == ConnectivityResult.none) { debugPrint('No internet connection, cannot subscribe to topic'); return; } try { await FirebaseMessaging.instance.subscribeToTopic(topic); debugPrint('successfully subscribed to $topic'); } catch (error, stackTrace) { debugPrint('an error occurred while subscribing to topic $topic \n $stackTrace \n error $error'); } }
4. 避免令牌操作和订阅冲突
从错误日志看,订阅过程中还调用了deleteToken,这可能干扰订阅流程。要么确保令牌操作完成后再订阅,要么调整两者的执行顺序,避免资源竞争。
内容的提问来源于stack exchange,提问作者Abdalla Tawfik

