Flutter Firebase Messaging后台消息处理空值异常求助
解决FCM后台消息处理的空指针错误
问题背景
我封装了PushNotificationService单例类处理所有通知逻辑,在main函数中初始化时遇到空指针错误,错误指向FirebaseMessaging注册后台消息处理器的位置。
通知服务代码
import 'package:dio/dio.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; @pragma('vm:entry-point') Future<void> _firebaseMessagingBackgroundHandler( RemoteMessage remoteMessage) async { await Firebase.initializeApp(); await PushNotificationService.instance.initializeNotification(); PushNotificationService.instance.showFlutterNotification(remoteMessage); debugPrint('Handling a background message ${remoteMessage.messageId}'); } class PushNotificationService { PushNotificationService._privateConstructor(); static final PushNotificationService instance = PushNotificationService._privateConstructor(); String? fcmToken; FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); final String serverKey = "MyServerKey"; late AndroidNotificationChannel channel; Future<void> initializeNotification() async { await Firebase.initializeApp(); channel = const AndroidNotificationChannel( 'high_importance_channel', // id 'High Importance Notifications', // title description: 'This channel is used for important notifications.', // description importance: Importance.high, enableVibration: true); await initializeLocalNotification(); FirebaseMessaging firebaseMessaging = FirebaseMessaging.instance; fcmToken = await firebaseMessaging.getToken(); debugPrint('FCM Token --> $fcmToken'); NotificationSettings notificationSettings = await firebaseMessaging.requestPermission( announcement: true, alert: true, badge: true, sound: true); debugPrint( 'Notification permission status : ${notificationSettings.authorizationStatus.name}'); if (notificationSettings.authorizationStatus == AuthorizationStatus.authorized) { FirebaseMessaging.onMessage.listen((RemoteMessage remoteMessage) async { debugPrint( 'Message title: ${remoteMessage.notification!.title}, body: ${remoteMessage.notification!.body}'); showFlutterNotification(remoteMessage); }); } FirebaseMessaging.onBackgroundMessage( (message) => _firebaseMessagingBackgroundHandler(message)); } Future<void> initializeLocalNotification() async { AndroidInitializationSettings android = const AndroidInitializationSettings('@mipmap/ic_launcher'); DarwinInitializationSettings ios = const DarwinInitializationSettings(); InitializationSettings platform = InitializationSettings(android: android, iOS: ios); await flutterLocalNotificationsPlugin.initialize(platform); } void showFlutterNotification(RemoteMessage message) { RemoteNotification? notification = message.notification; AndroidNotification? android = message.notification?.android; if (notification != null && android != null && !kIsWeb) { flutterLocalNotificationsPlugin.show( notification.hashCode, notification.title, notification.body, NotificationDetails( android: AndroidNotificationDetails(channel.id, channel.name, channelDescription: channel.description, icon: '@mipmap/ic_launcher', priority: Priority.high, importance: Importance.high), ), ); } } Future<void> sendNotification(String fcmToken, String msg) async { Dio dio = Dio(); dio.post( 'https://fcm.googleapis.com/fcm/send', options: Options( headers: { "Content-Type": "application/json", "Authorization": "key=$serverKey" }, ), data: { "to": fcmToken, "notification": { "title": "AppName", "body": msg, }, }, ); } }
main函数初始化代码
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); await MobileAds.instance.initialize(); //push notification service await PushNotificationService.instance.initializeNotification(); runApp( EasyDynamicThemeWidget( child: const MyApp(), ), ); }
错误日志
E/flutter (13038): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Null check operator used on a null value E/flutter (13038): #0 MethodChannelFirebaseMessaging.registerBackgroundMessageHandler (package:firebase_messaging_platform_interface/src/method_channel/method_channel_messaging.dart:201:53) E/flutter (13038): #1 FirebaseMessagingPlatform.onBackgroundMessage= (package:firebase_messaging_platform_interface/src/platform_interface/platform_interface_messaging.dart:107:16) E/flutter (13038): #2 FirebaseMessaging.onBackgroundMessage (package:firebase_messaging/src/messaging.dart:73:31) E/flutter (13038): #3 PushNotificationService.initializeNotification (package:app_user/services/push_notification_service.dart:56:25) E/flutter (13038): <asynchronous suspension> E/flutter (13038): #4 main (package:app_user/main.dart:28:3) E/flutter (13038): <asynchronous suspension>
环境信息
- Flutter版本:3.10.6(stable通道)
- 系统:Windows 10 19045.3324
- Android SDK版本:30.0.3
- 使用的依赖包版本:
- firebase_auth: ^4.7.3
- firebase_core: ^2.15.1
- firebase_messaging: ^14.6.7
- flutter_local_notifications: ^15.1.1
解决方案
问题根源
- 后台消息处理器
_firebaseMessagingBackgroundHandler中重复调用initializeNotification(),导致重复注册FirebaseMessaging.onBackgroundMessage,引发空指针。 main函数中已经初始化过Firebase.initializeApp(),通知服务类中再次初始化属于冗余操作。- 后台消息处理器中不需要初始化整个通知服务,只需初始化本地通知插件即可。
修改步骤
- 调整后台消息处理器:移除重复的通知服务初始化,仅初始化本地通知并显示消息:
@pragma('vm:entry-point') Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage remoteMessage) async { await Firebase.initializeApp(); // 仅初始化本地通知,不要调用整个initializeNotification final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); const AndroidInitializationSettings android = AndroidInitializationSettings('@mipmap/ic_launcher'); const DarwinInitializationSettings ios = DarwinInitializationSettings(); const InitializationSettings platform = InitializationSettings(android: android, iOS: ios); await flutterLocalNotificationsPlugin.initialize(platform); // 创建临时通知通道(后台环境无法访问类成员) const channel = AndroidNotificationChannel( 'high_importance_channel', 'High Importance Notifications', description: 'This channel is used for important notifications.', importance: Importance.high, enableVibration: true); RemoteNotification? notification = remoteMessage.notification; AndroidNotification? androidNotif = remoteMessage.notification?.android; if (notification != null && androidNotif != null && !kIsWeb) { flutterLocalNotificationsPlugin.show( notification.hashCode, notification.title, notification.body, NotificationDetails( android: AndroidNotificationDetails(channel.id, channel.name, channelDescription: channel.description, icon: '@mipmap/ic_launcher', priority: Priority.high, importance: Importance.high), ), ); } debugPrint('Handling a background message ${remoteMessage.messageId}'); }
- 修改通知服务初始化方法:
- 移除重复的
Firebase.initializeApp()调用; - 将
FirebaseMessaging.onBackgroundMessage的注册移到方法最外层,避免权限判断导致的注册遗漏; - 确保通道创建后调用
flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(channel);来创建通知通道。
- 移除重复的
修改后的initializeNotification方法:
Future<void> initializeNotification() async { channel = const AndroidNotificationChannel( 'high_importance_channel', // id 'High Importance Notifications', // title description: 'This channel is used for important notifications.', // description importance: Importance.high, enableVibration: true); // 创建通知通道 await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(channel); await initializeLocalNotification(); FirebaseMessaging firebaseMessaging = FirebaseMessaging.instance; fcmToken = await firebaseMessaging.getToken(); debugPrint('FCM Token --> $fcmToken'); NotificationSettings notificationSettings = await firebaseMessaging.requestPermission( announcement: true, alert: true, badge: true, sound: true); debugPrint( 'Notification permission status : ${notificationSettings.authorizationStatus.name}'); FirebaseMessaging.onMessage.listen((RemoteMessage remoteMessage) async { debugPrint( 'Message title: ${remoteMessage.notification!.title}, body: ${remoteMessage.notification!.body}'); showFlutterNotification(remoteMessage); }); // 移到权限判断外,确保一定会注册后台处理器 FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); }
- main函数保持不变,但确保
Firebase.initializeApp()只调用一次。
额外注意事项
- 后台消息处理器必须是顶级函数,不能是类成员或匿名函数,你当前的写法符合要求;
- 后台环境中无法访问单例类的成员变量,所以需要在处理器内部重新初始化必要组件;
- 确保AndroidManifest.xml中配置了正确的FCM相关权限和服务。
内容的提问来源于stack exchange,提问作者asd1001
相关产品推荐
相关产品推荐

