iOS Flutter集成FCM重启重复接收旧通知问题求助
解决FCM重启App重复推送旧通知的问题
问题原因
在iOS平台上,FCM通知分为显示通知(带alert字段)和静默数据通知两类。如果是显示通知,实际由APNs负责推送,若未正确标记通知已读,或是App初始化时重复触发旧通知的处理逻辑,就会导致重复接收。另外你的代码存在两处隐患:前台监听onMessage时直接调用后台处理函数,以及后台处理逻辑里重复初始化Firebase,这些都可能引发重复推送问题。
具体解决步骤
1. 给通知添加“已处理”标记,避免重复执行逻辑
用SharedPreferences存储已处理过的通知ID,每次处理通知前先检查是否已处理,处理完成后标记状态,防止重启后重复触发:
// 先在pubspec.yaml添加依赖:shared_preferences: ^2.2.2 import 'package:shared_preferences/shared_preferences.dart'; // 检查通知是否已处理 Future<bool> isMessageProcessed(String? messageId) async { if (messageId == null) return false; SharedPreferences prefs = await SharedPreferences.getInstance(); return prefs.getBool('processed_$messageId') ?? false; } // 标记通知为已处理 Future<void> markMessageProcessed(String? messageId) async { if (messageId == null) return; SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setBool('processed_$messageId', true); }
2. 优化Firebase初始化与通知监听逻辑
修改初始化代码,去掉冗余的Firebase初始化操作,同时正确处理前台、后台以及App启动时的通知:
void main() async { WidgetsFlutterBinding.ensureInitialized(); await MyDbModel().initializeDB(); await initFireBase(); // 用await确保Firebase初始化完成再启动App runApp(myApp()); } void initFireBase() async { print("------------- InitFireBase -------------"); await Firebase.initializeApp(); // 请求iOS通知权限(必须步骤) await FirebaseMessaging.instance.requestPermission( alert: true, badge: true, sound: true, ); // 处理前台收到的通知 FirebaseMessaging.onMessage.listen((RemoteMessage message) async { if (!await isMessageProcessed(message.messageId)) { await updateDB(message); await markMessageProcessed(message.messageId); // iOS前台默认不弹通知,需要调用本地通知插件展示 await showLocalNotification(message); } }); // 处理App从杀死状态点击通知启动的场景 RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); if (initialMessage != null) { if (!await isMessageProcessed(initialMessage.messageId)) { await updateDB(initialMessage); await markMessageProcessed(initialMessage.messageId); } } // 注册后台消息处理回调 FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); } // 单独抽离DB更新逻辑,和通知处理解耦 Future<void> updateDB(RemoteMessage message) async { // 这里写你的数据库更新代码 } // 后台消息处理函数:去掉重复的Firebase初始化 Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async { if (!await isMessageProcessed(message.messageId)) { await updateDB(message); await markMessageProcessed(message.messageId); } } // 前台展示本地通知的方法(iOS专属) Future<void> showLocalNotification(RemoteMessage message) async { final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); // 初始化iOS通知设置 const iosSettings = IOSInitializationSettings(); await flutterLocalNotificationsPlugin.initialize(const InitializationSettings(iOS: iosSettings)); if (message.notification != null) { await flutterLocalNotificationsPlugin.show( message.messageId.hashCode, message.notification?.title, message.notification?.body, const NotificationDetails( iOS: IOSNotificationDetails(), ), ); } }
3. 关键注意事项
- 删掉后台处理函数中的
await Firebase.initializeApp();:后台消息触发时Firebase已完成初始化,重复调用会引发状态异常; - iOS前台通知需手动展示:APNs默认不在App前台弹出通知,必须通过本地通知插件实现;
- 必须用
messageId做唯一标识:每个FCM通知都有唯一的messageId,以此判断是否已处理是防止重复的核心; initFireBase要加await:确保Firebase初始化完成后再启动App,避免启动时漏处理通知状态。
总结
核心解决思路就是给每个通知加唯一标识并标记处理状态,同时优化Firebase的初始化和通知监听逻辑,避免重复执行处理代码,这样重启App后就不会再收到旧通知了。
内容的提问来源于stack exchange,提问作者Fugipe
相关产品推荐
相关产品推荐

