Node.js云函数无FCM通知问题排查及VS Code调试咨询
问题解答
1. 是否需要将云函数与Android Studio关联?
不需要。云函数部署在Firebase云端独立运行,负责监听数据库事件并发送FCM通知;Android端仅需确保设备正确获取FCM令牌并上传到指定数据库节点(/Registered Admins/{uid}/Token),同时配置好FCM接收逻辑即可,两者无直接关联需求。
2. VS Code中调试云函数的步骤
- 确保本地已安装
firebase-tools:执行npm install -g firebase-tools - 在VS Code中打开云函数项目根目录,安装依赖:执行
npm install - 配置调试环境:
- 点击左侧「运行和调试」面板,选择「创建launch.json文件」,选择「Node.js」环境
- 将launch.json内容替换为:
{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "attach", "name": "Attach to Firebase Functions", "port": 9229, "restart": true, "skipFiles": ["<node_internals>/**"] } ] }
- 启动本地云函数模拟器:执行
firebase emulators:start --inspect-functions - 在VS Code中点击「启动调试」,即可在代码中设置断点调试,同时可通过Firebase模拟器控制台触发数据库事件测试。
代码问题排查(未收到通知的核心原因)
你的代码存在关键逻辑错误,修正如下:
- 数据库路径错误:原代码中
/Registered Admins/{uid}/Token的{uid}是占位符,未遍历所有管理员节点收集令牌,需改为遍历整个/Registered Admins节点 - 无效令牌清理逻辑优化:原代码无法精准定位无效令牌对应的管理员节点,需调整匹配逻辑
修正后的完整代码:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.listen = functions.database.ref("/Emergencies/{pushId}") .onCreate(async (change, context) => { const emergencyData = change.after.val(); const pushId = context.params.pushId; // 获取所有管理员的设备令牌 const tokensSnapshot = await admin.database().ref("/Registered Admins").once("value"); const tokens = []; tokensSnapshot.forEach(childSnapshot => { const token = childSnapshot.child("Token").val(); if (token) { tokens.push(token); } }); if (tokens.length === 0) { functions.logger.log("没有可发送通知的设备令牌"); return null; } functions.logger.log("共有", tokens.length, "个令牌需要发送通知"); // 通知内容 const payload = { notification: { title: "新的紧急求助请求!", body: "有人需要帮助,请立即查看Emergenie App!", } }; // 发送通知 const response = await admin.messaging().sendToDevice(tokens, payload); const tokensToRemove = []; response.results.forEach((result, index) => { const error = result.error; if (error) { functions.logger.error("发送通知失败到令牌", tokens[index], error); // 清理无效令牌 if (error.code === 'messaging/invalid-registration-token' || error.code === 'messaging/registration-token-not-registered') { tokensSnapshot.forEach(childSnapshot => { if (childSnapshot.child("Token").val() === tokens[index]) { tokensToRemove.push(childSnapshot.child("Token").remove()); } }); } } }); return Promise.all(tokensToRemove); });
内容的提问来源于stack exchange,提问作者Timow
相关产品推荐
相关产品推荐

