如何通过Firebase云函数向所有用户推送通知?现有实现遇阻求排查
如何使用Firebase Cloud Messaging向所有用户推送通知?
我之前通过设备的FCM Token,使用
admin.messaging().sendToDevice(tokens, payload)方法向特定用户推送通知。现在我尝试向所有用户推送通知,但无法成功。以下是我的实现代码,请问该实现是否正确?若错误,该如何达成需求?
const payload = { notification: { title: 'Hey! ', body: `Check Choice of the day.`, sound: 'default', icon: '', type: 'Editorial', badge : '1' } }; return admin.messaging().sendToTopic("News",payload) .then(function(response){ console.log('Notification sent successfully:',response); }) .catch(function(error){ console.log('Notification sent failed:',error); });
你的代码问题分析
从语法层面看,这段代码是没问题的,但为什么收不到推送?核心原因是:sendToTopic只能推送给主动订阅了该主题的设备。如果你的用户设备没有在客户端代码里订阅"News"这个主题,自然就收不到你发的通知。
两种实现向所有用户推送的方案
方案1:基于主题推送(推荐)
主题推送是FCM官方推荐的大规模用户推送方式,步骤分为两步:
- 在客户端让所有用户设备订阅"News"主题
- 服务端用你现在的代码发送主题通知
以下是各平台的客户端订阅代码示例:
- Android(Kotlin):
FirebaseMessaging.getInstance().subscribeToTopic("News") .addOnCompleteListener { task -> val msg = if (task.isSuccessful) "Subscribed to News topic" else "Subscribe failed" Log.d(TAG, msg) }
- iOS(Swift):
Messaging.messaging().subscribe(toTopic: "News") { error in let status = error?.localizedDescription ?? "Successfully subscribed to News topic" print(status) }
- Web(JavaScript):
messaging.subscribeToTopic('News') .then(() => console.log('Subscribed to News topic successfully')) .catch(error => console.error('Topic subscription failed:', error));
完成客户端订阅后,你当前的服务端代码就能正常推送给所有订阅了"News"主题的用户了。
方案2:批量发送到所有用户Token(适合小规模用户)
如果不想用主题,你可以先从数据库中收集所有用户的FCM Token,再用sendToDevice批量发送。但要注意:sendToDevice一次最多支持1000个Token,用户量超过这个数的话需要分批处理。
示例代码:
// 第一步:从数据库获取所有有效的FCM Token const getAllValidTokens = async () => { // 这里替换成你的数据库查询逻辑,比如从Firestore的users集合获取 const userSnapshot = await admin.firestore().collection('users').get(); // 过滤掉空的或无效的Token return userSnapshot.docs .map(doc => doc.data().fcmToken) .filter(token => token && typeof token === 'string'); }; // 第二步:分批发送通知 const sendNotificationToAllUsers = async () => { const payload = { notification: { title: 'Hey! ', body: `Check Choice of the day.`, sound: 'default', icon: '', type: 'Editorial', badge : '1' } }; const allTokens = await getAllValidTokens(); const batchSize = 1000; // FCM单次发送上限 for (let i = 0; i < allTokens.length; i += batchSize) { const currentBatch = allTokens.slice(i, i + batchSize); try { const response = await admin.messaging().sendToDevice(currentBatch, payload); console.log(`Batch ${Math.floor(i/batchSize) + 1} sent successfully`, response); // 可选:处理无效Token,从数据库中移除 const invalidTokens = []; response.results.forEach((result, index) => { const error = result.error; if (error) { console.error(`Failed to send to token ${currentBatch[index]}`, error); // 标记无效Token(比如过期或未注册的) if (['messaging/invalid-registration-token', 'messaging/registration-token-not-registered'].includes(error.code)) { invalidTokens.push(currentBatch[index]); } } }); // 这里可以添加删除无效Token的逻辑 if (invalidTokens.length > 0) { console.log('Removing invalid tokens:', invalidTokens); // 示例:从Firestore删除对应Token的用户记录或更新Token字段 invalidTokens.forEach(token => { admin.firestore().collection('users').where('fcmToken', '==', token).get() .then(snapshot => snapshot.docs.forEach(doc => doc.ref.update({fcmToken: null}))); }); } } catch (error) { console.error(`Batch ${Math.floor(i/batchSize) + 1} send failed`, error); } } }; return sendNotificationToAllUsers();
⚠️ 注意:当用户量很大时,这种方法会消耗更多资源,且容易触发FCM的配额限制,优先选择主题推送更高效稳定。
内容的提问来源于stack exchange,提问作者isuru
相关产品推荐
相关产品推荐

