Android设备间通知推送实现及Firebase Cloud Functions报错排查
一、修复Cloud Functions中的TypeError问题
你遇到的Cannot read property 'pharmacien_id' of undefined错误主要有两个明确的原因,我帮你逐一排查并修正:
1. 文档路径中的空格导致参数解析失败
看你写的监听路径:"Pharmaciens/{pharmacien_id} /Commandes/{commande_id}" —— 在{pharmacien_id}后面多了一个空格,这会让Firebase无法正确识别路径参数,自然就找不到pharmacien_id了,这是最直接的诱因。
2. Firebase Functions版本的参数结构变化
在较新的Firebase Functions版本中,onWrite的回调参数已经从旧版的event改为(change, context),原来的event.params已经迁移到context.params里。旧写法在新版本环境下会直接导致event.params为undefined。
修正后的完整代码
'use strict'; const functions = require('firebase-functions'); const admin = require('firebase-admin'); // 新版本不需要传入functions.config().firebase,initializeApp会自动完成初始化 admin.initializeApp(); exports.sendCommandes = functions.firestore .document("Pharmaciens/{pharmacien_id}/Commandes/{commande_id}") // 移除路径中的空格 .onWrite((change, context) => { // 使用新版参数结构 const pharmacien_id = context.params.pharmacien_id; const commande_id = context.params.commande_id; console.log(`pharmacien_id: ${pharmacien_id} | commande_id: ${commande_id}`); // 函数需要返回Promise或null,避免云端执行超时 return null; });
二、Android用户间通知推送的实现步骤
要实现Android设备间的用户定向通知推送,核心是结合FCM(Firebase Cloud Messaging)和Firestore存储用户令牌,具体步骤如下:
1. 把FCM集成到Android项目
确保你的Android项目已经正确配置Firebase和FCM:
- 在Firebase控制台为项目添加Android应用,下载
google-services.json放到项目根目录。 - 在模块级
build.gradle中添加FCM依赖(建议用最新版本):implementation 'com.google.firebase:firebase-messaging:23.4.0'
2. 存储用户的FCM令牌
用户打开APP时,获取他们的FCM令牌并保存到Firestore的用户文档中(比如Pharmaciens/{userId}下新增fcmToken字段):
// 在Android代码中获取并保存令牌 FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> if (!task.isSuccessful) { Log.w("FCM", "获取FCM令牌失败", task.exception) return@addOnCompleteListener } // 获取到的FCM令牌 val token = task.result // 将令牌写入当前登录用户的Firestore文档 FirebaseFirestore.getInstance() .collection("Pharmaciens") .document(currentUserId) // 替换为当前用户的ID .update("fcmToken", token) .addOnSuccessListener { Log.d("FCM", "令牌保存成功") } .addOnFailureListener { e -> Log.w("FCM", "令牌保存失败", e) } }
3. 在Cloud Functions中触发推送逻辑
当目标事件触发(比如新增订单)时,从Firestore获取目标用户的FCM令牌,调用FCM接口发送通知:
// 延续之前的sendCommandes函数,添加推送逻辑 exports.sendCommandes = functions.firestore .document("Pharmaciens/{pharmacien_id}/Commandes/{commande_id}") .onWrite(async (change, context) => { const pharmacienId = context.params.pharmacien_id; // 1. 从Firestore获取目标用户的FCM令牌 const userDoc = await admin.firestore().collection("Pharmaciens").doc(pharmacienId).get(); if (!userDoc.exists) { console.log("未找到用户文档"); return null; } const fcmToken = userDoc.data().fcmToken; if (!fcmToken) { console.log("该用户未绑定FCM令牌"); return null; } // 2. 构建通知内容 const payload = { notification: { title: "新订单提醒", body: "你有一个新的订单需要处理", clickAction: "OPEN_ORDER_ACTIVITY" // 点击通知要打开的Activity }, token: fcmToken }; // 3. 发送通知 try { await admin.messaging().send(payload); console.log("通知发送成功"); return null; } catch (error) { console.error("发送通知失败:", error); return null; } });
4. Android端接收并展示通知
创建一个继承自FirebaseMessagingService的类,处理通知的接收和展示:
class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) // 解析通知内容并展示 remoteMessage.notification?.let { sendNotification(it.title, it.body) } } private fun sendNotification(title: String?, body: String?) { // 点击通知打开订单页面 val intent = Intent(this, OrderActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) // 适配Android O及以上的通知渠道 val channelId = "order_notification_channel" val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) val notificationBuilder = NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.ic_notification) .setContentTitle(title) .setContentText(body) .setAutoCancel(true) .setSound(defaultSoundUri) .setContentIntent(pendingIntent) val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(channelId, "订单通知", NotificationManager.IMPORTANCE_DEFAULT) notificationManager.createNotificationChannel(channel) } notificationManager.notify(0, notificationBuilder.build()) } }
最后别忘了在AndroidManifest.xml中注册这个服务:
<service android:name=".MyFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
内容的提问来源于stack exchange,提问作者yoyo guen

