You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Firebase Cloud Functions实现设备间消息推送(排除发送者/定向推送)

没问题,我来帮你搞定这个需求!下面是完整的实现方案,包含Android客户端(Java)和Firebase云函数的代码示例,完全匹配你提到的场景:

整体实现思路

核心流程是:Android客户端收集发送者ID、通知内容(可选指定接收者),调用Firebase云函数;云函数负责查询目标用户的FCM推送令牌,排除发送者后,调用FCM API完成通知推送。

Android Java客户端代码

首先确保你已经集成了Firebase Auth、Firebase Functions和Firebase Messaging SDK。下面是发送通知的核心代码:

import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.functions.FirebaseFunctions;
import com.google.firebase.functions.FirebaseFunctionsException;
import java.util.HashMap;

public class NotificationSenderActivity extends AppCompatActivity {

    private FirebaseFunctions firebaseFunctions;
    private FirebaseAuth firebaseAuth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_notification_sender);

        firebaseFunctions = FirebaseFunctions.getInstance();
        firebaseAuth = FirebaseAuth.getInstance();

        // 绑定发送按钮点击事件
        Button sendBtn = findViewById(R.id.btn_send_notification);
        sendBtn.setOnClickListener(v -> triggerNotificationSend());
    }

    // 触发通知发送逻辑,支持广播或定向推送
    private void triggerNotificationSend() {
        // 获取当前发送者的UID
        String senderUid = firebaseAuth.getCurrentUser().getUid();
        // 通知内容
        String notificationTitle = "新消息提醒";
        String notificationBody = "这是来自用户A的测试通知";

        // 定向推送时填写目标用户UID,广播推送时设为null
        String targetUserUid = "user_B_uid"; // 广播时改为 null

        // 调用云函数
        firebaseFunctions.getHttpsCallable("sendCustomNotification")
                .call(new HashMap<String, Object>() {{
                    put("senderUid", senderUid);
                    put("title", notificationTitle);
                    put("body", notificationBody);
                    put("targetUid", targetUserUid);
                }})
                .addOnCompleteListener(task -> {
                    if (task.isSuccessful()) {
                        Toast.makeText(this, "通知发送成功", Toast.LENGTH_SHORT).show();
                    } else {
                        Exception e = task.getException();
                        if (e instanceof FirebaseFunctionsException) {
                            FirebaseFunctionsException ffe = (FirebaseFunctionsException) e;
                            Toast.makeText(this, "发送失败:" + ffe.getDetails(), Toast.LENGTH_LONG).show();
                        }
                    }
                });
    }
}

补充:FCM令牌存储代码

用户登录或App启动时,需要将FCM令牌保存到Firestore,供云函数查询使用:

import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.messaging.FirebaseMessaging;
import android.util.Log;

// 在用户登录成功后调用
private void saveFcmToken() {
    FirebaseMessaging.getInstance().getToken()
            .addOnCompleteListener(task -> {
                if (!task.isSuccessful()) {
                    Log.w("FCM", "获取令牌失败", task.getException());
                    return;
                }
                String fcmToken = task.getResult();
                // 将令牌保存到Firestore的users集合中,以用户UID为文档ID
                FirebaseFirestore.getInstance().collection("users")
                        .doc(firebaseAuth.getCurrentUser().getUid())
                        .update("fcmToken", fcmToken)
                        .addOnSuccessListener(aVoid -> Log.d("FCM", "令牌保存成功"))
                        .addOnFailureListener(e -> Log.w("FCM", "令牌保存失败", e));
            });
}

Firebase Cloud Functions代码(Node.js)

首先确保你已经初始化了Firebase云函数项目,然后在index.js中添加以下代码:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

// 云函数:处理自定义通知推送
exports.sendCustomNotification = functions.https.onCall(async (data, context) => {
    // 验证用户身份(可选但建议添加)
    if (!context.auth) {
        throw new functions.https.HttpsError("unauthenticated", "请先登录");
    }

    const { senderUid, title, body, targetUid } = data;
    let targetTokens = [];

    if (targetUid) {
        // 定向推送:获取指定用户的FCM令牌
        const userDoc = await admin.firestore().collection("users").doc(targetUid).get();
        if (userDoc.exists && userDoc.data().fcmToken) {
            targetTokens.push(userDoc.data().fcmToken);
        } else {
            throw new functions.https.HttpsError("not-found", "目标用户不存在或未注册推送令牌");
        }
    } else {
        // 广播推送:获取所有用户令牌,排除发送者
        const usersSnapshot = await admin.firestore().collection("users").get();
        usersSnapshot.forEach(doc => {
            const uid = doc.id;
            const token = doc.data().fcmToken;
            if (uid !== senderUid && token) {
                targetTokens.push(token);
            }
        });
    }

    if (targetTokens.length === 0) {
        throw new functions.https.HttpsError("no-recipients", "没有找到有效的接收者");
    }

    // 构建FCM通知 payload
    const payload = {
        notification: {
            title: title,
            body: body
        },
        data: {
            senderUid: senderUid // 可选:传递发送者ID,方便客户端处理
        }
    };

    // 发送通知
    try {
        const response = await admin.messaging().sendToDevice(targetTokens, payload);
        functions.logger.log("通知发送成功,响应:", response);
        return { success: true, message: `已推送给 ${targetTokens.length} 个设备` };
    } catch (error) {
        functions.logger.error("通知发送失败:", error);
        throw new functions.https.HttpsError("internal", "推送过程出错", error);
    }
});

场景验证说明

  • 广播推送场景:当用户A发送通知且targetUid为null时,云函数会遍历所有用户的令牌,排除A自己的令牌后推送给B、C等其他用户,A不会收到通知提醒。
  • 定向推送场景:当A指定targetUid为B的UID时,云函数只会获取B的令牌并推送,仅B能收到通知。

内容的提问来源于stack exchange,提问作者Jon Doe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:59:48