无需Cloud Functions实现Android端Firebase设备间推送通知
Android端FCM设备间推送实现方案(无付费Cloud Functions+不暴露Server Key)
核心思路是借助Firebase实时数据库/Cloud Firestore作为消息中转层,利用数据库的实时监听能力触发本地通知,全程无需调用FCM服务器端API(也就不需要Server Key),同时通过安全规则保障数据访问权限。
1. 配置数据库安全规则
首先要限制消息节点的读写权限,确保只有对应UID的用户能访问自己的消息队列,避免越权操作。
Realtime Database规则示例
{ "rules": { "user_messages": { "$uid": { ".read": "$uid === auth.uid", ".write": "$uid === auth.uid" } }, "users": { "$uid": { ".read": "$uid === auth.uid || auth != null", ".write": "$uid === auth.uid" } } } }
user_messages/$uid:仅当前用户可读写自己的消息队列users/$uid:允许其他用户读取公开信息(比如设备令牌),但仅本人可修改
2. 设备令牌存储(可选但实用)
让用户登录后将自己的FCM设备令牌存入数据库,方便后续扩展(比如离线消息处理):
FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> if (task.isSuccessful) { val token = task.result ?: return@addOnCompleteListener val currentUid = FirebaseAuth.getInstance().currentUser?.uid ?: return@addOnCompleteListener FirebaseDatabase.getInstance().getReference("users/$currentUid/fcm_token") .setValue(token) } }
3. 发送方:写入消息到接收方队列
发送方只需将消息内容写入接收方的专属消息节点,无需直接调用FCM推送API:
val senderUid = FirebaseAuth.getInstance().currentUser?.uid ?: return val receiverUid = "目标用户UID" val messageData = hashMapOf( "senderUid" to senderUid, "title" to "好友消息", "content" to "这是设备间直接推送的内容", "timestamp" to System.currentTimeMillis() ) // 写入接收方的消息队列 FirebaseDatabase.getInstance().getReference("user_messages/$receiverUid") .push() .setValue(messageData) .addOnSuccessListener { // 消息写入成功回调 } .addOnFailureListener { // 处理写入失败 }
4. 接收方:监听消息并触发本地通知
接收方监听自己的消息节点,一旦有新消息写入,直接调用Android本地通知API显示通知,全程不依赖FCM服务器下行推送:
val currentUid = FirebaseAuth.getInstance().currentUser?.uid ?: return val messagesRef = FirebaseDatabase.getInstance().getReference("user_messages/$currentUid") // 监听消息节点的新增事件 messagesRef.addChildEventListener(object : ChildEventListener { override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) { val message = snapshot.getValue(Message::class.java) ?: return // 显示本地通知 showLocalNotification(message.title, message.content) // 可选:删除已处理的消息,避免重复通知 snapshot.ref.removeValue() } // 实现其他接口方法(可留空) override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {} override fun onChildRemoved(snapshot: DataSnapshot) {} override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {} override fun onCancelled(error: DatabaseError) {} }) // 本地通知实现 private fun showLocalNotification(title: String, content: String) { val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // 创建通知渠道(Android O及以上必填) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( "DEVICE_PUSH_CHANNEL", "设备间推送", NotificationManager.IMPORTANCE_DEFAULT ) notificationManager.createNotificationChannel(channel) } // 构建通知内容 val intent = Intent(this, MainActivity::class.java) val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) val notification = NotificationCompat.Builder(this, "DEVICE_PUSH_CHANNEL") .setContentTitle(title) .setContentText(content) .setSmallIcon(R.drawable.ic_notification) .setContentIntent(pendingIntent) .setAutoCancel(true) .build() // 显示通知 notificationManager.notify(System.currentTimeMillis().toInt(), notification) } // 消息数据类 data class Message( val senderUid: String? = null, val title: String? = null, val content: String? = null, val timestamp: Long? = null )
核心优势
- 无付费成本:利用Firebase数据库免费额度即可满足小型应用需求
- 无Server Key暴露风险:全程使用Firebase客户端SDK操作,无需调用FCM服务器端API
- 安全可控:通过数据库规则严格限制消息访问权限,避免数据泄露
内容的提问来源于stack exchange,提问作者lanezalbryt
相关产品推荐
相关产品推荐

