Flutter Web集成FCM重复推送及多Service Worker问题排查
我正在开发一款面向PWA(需在移动设备安装)的Web应用,原本计划做原生iOS应用,因上架问题转为全Flutter Web开发。集成FCM后,多设备(包括PC端Edge)都出现重复接收推送的问题。
现有代码情况
index.html中的Service Worker注册脚本
<script> if ('serviceWorker' in navigator) { window.addEventListener('load', function () { navigator.serviceWorker.register('/firebase-messaging-sw.js'); }); } </script>
firebase-messaging-sw.js配置代码
importScripts('https://www.gstatic.com/firebasejs/10.7.1/firebase-app-compat.js'); importScripts('https://www.gstatic.com/firebasejs/10.7.1/firebase-messaging-compat.js'); const firebaseConfig = { apiKey: "xxxx", authDomain: "xxxxx", databaseURL: "xxxx", projectId: "xxxxx", storageBucket: "xxxx", messagingSenderId: "xxxxx", appId: "xxxxxx" }; firebase.initializeApp(firebaseConfig); const messaging = firebase.messaging(); let darkPatternsValue = 0; self.addEventListener('message', function(event){ if (event.data && event.data.type === 'SET_DARK_PATTERNS_VALUE') { darkPatternsValue = event.data.value; } }); messaging.onBackgroundMessage(function(payload) { console.log('Received background message ', payload); if(darkPatternsValue != 1){ return; } lastNotificationId = currentNotificationId; const notificationTitle = payload.notification.title; const notificationOptions = { body: payload.notification.body, }; return self.registration.showNotification(notificationTitle, notificationOptions); }); self.addEventListener('notificationclick', function(event) { console.log('Notification clicked ', event); event.notification.close(); event.waitUntil( clients.openWindow('/?source=notification') ); });
Flutter中向Service Worker发送配置的代码
void sendDarkPatternsValue() async { html.window.navigator.serviceWorker?.getRegistrations().then((registrations) { for (final registration in registrations) { registration.active?.postMessage({'type': 'SET_DARK_PATTERNS_VALUE', 'value': 1}); } }); }
当前问题现象
现在存在两个运行的Service Worker:一个是根路径的http://localhost:xxxxxx/,另一个是http://localhost:xxxxxx/firebase-cloud-messaging-push-scope。
- 当
darkPatternsValue设为0时,仍会收到1条无法触发notificationclick事件的推送; - 当
darkPatternsValue设为1时,会收到2条推送,仅第二条能触发点击事件。
问题原因
firebase-cloud-messaging-push-scope是Firebase SDK在某些场景下自动注册的专用Service Worker,用于处理推送消息。但如果手动注册了自定义的firebase-messaging-sw.js却未明确指定scope,Firebase会认为没有可用的Worker来处理推送,从而自动创建这个带push-scope的Worker,最终导致两个Worker同时监听推送事件,产生重复通知。
另外,向Worker发送darkPatternsValue时,虽然遍历了所有注册的Worker,但可能存在其中一个Worker未正确接收设置值的情况,导致部分通知不受开关控制。
解决步骤
1. 统一Service Worker注册逻辑
修改index.html中的注册代码,明确指定scope为根路径,让Firebase使用你自定义的Worker,避免自动创建额外的Worker:
<script> if ('serviceWorker' in navigator) { window.addEventListener('load', function () { navigator.serviceWorker.register('/firebase-messaging-sw.js', { scope: '/' }); }); } </script>
或者,直接移除index.html中的手动注册代码,让Firebase SDK自动关联你的firebase-messaging-sw.js(确保文件放在根目录)。
2. 清理已存在的重复Service Worker
打开浏览器开发者工具(F12),切换到Application > Service Workers面板,找到firebase-cloud-messaging-push-scope对应的Worker,点击「Unregister」按钮手动注销。刷新页面后,确认只有根路径的Worker在运行。
3. 优化消息处理逻辑
在firebase-messaging-sw.js的onBackgroundMessage回调中,可添加消息ID的唯一性校验,避免同一消息被重复处理:
const processedMessages = new Set(); messaging.onBackgroundMessage(function(payload) { const messageId = payload.message_id; if (processedMessages.has(messageId)) return; processedMessages.add(messageId); console.log('Received background message ', payload); if(darkPatternsValue != 1){ return; } const notificationTitle = payload.notification.title; const notificationOptions = { body: payload.notification.body, }; return self.registration.showNotification(notificationTitle, notificationOptions); });
4. 确保Flutter端FCM初始化正确
在Flutter Web的FCM初始化代码中,配置正确的VAPID密钥,确保SDK能正确识别自定义Service Worker:
import 'package:firebase_messaging/firebase_messaging.dart'; void initFCM() async { await FirebaseMessaging.instance.setAutoInitEnabled(true); // 替换为你的VAPID密钥,可在Firebase控制台 > 项目设置 > 云消息传递中获取 await FirebaseMessaging.instance.getToken(vapidKey: 'YOUR_VAPID_KEY'); }
内容的提问来源于stack exchange,提问作者Felpower

