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

类WhatsApp聊天应用如何通过FCM实现向群组成员批量推送通知?

实现群组通知推送的两种可行方案

方案1:使用FCM主题订阅(推荐,实现最简单)

FCM的主题订阅功能天生适配群组通知场景,实现逻辑如下:

  • 每个群组对应一个唯一的主题标识,命名规则可自定义,例如group_{群组ID}
  • 用户加入群组时,客户端调用订阅接口:
// 加入群组时订阅对应主题
FirebaseMessaging.getInstance().subscribeToTopic("group_" + groupId)
        .addOnCompleteListener(task -> {
            String msg = "订阅群组通知成功";
            if (!task.isSuccessful()) {
                msg = "订阅群组通知失败";
            }
            Log.d(TAG, msg);
        });
  • 用户退出群组时,调用取消订阅接口避免继续收到该群通知:
// 退出群组时取消订阅
FirebaseMessaging.getInstance().unsubscribeFromTopic("group_" + groupId);
  • 发送群组通知时,修改原有发通知的逻辑,把to参数替换为topic参数即可,修改后的代码如下:
void sendGroupNotification(String name, String message, String groupId){
    try {
        RequestQueue queue = Volley.newRequestQueue(this);
        String url = "https://fcm.googleapis.com/fcm/send";
        JSONObject data = new JSONObject();
        data.put("title", name);
        data.put("body", message);
        // 可额外添加自定义参数,比如群ID,方便点击通知跳转到对应群聊
        data.put("groupId", groupId);
        
        JSONObject notificationData =new JSONObject();
        notificationData.put("notification",data);
        // 替换to参数为topic,值为对应群组的主题名
        notificationData.put("topic", "group_" + groupId);

        JsonObjectRequest request = new JsonObjectRequest(url, notificationData
                , new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                Toast.makeText(ChatActivity.this,"群组通知发送成功", Toast.LENGTH_SHORT).show();
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(ChatActivity.this,error.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
            }
        }){
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                HashMap<String,String> map =new HashMap<>();
                // 注意:此处硬编码FCM服务端密钥非常危险,仅可作为临时测试用,正式环境必须把发通知逻辑放到后端实现
                String key ="Key=你的FCM服务端密钥";
                map.put("Authorization",key);
                map.put("Content-Type","application/json");
                return map;
            }
        };
        queue.add(request);
    }catch (Exception ex){
        ex.printStackTrace();
    }
}
  • 接收端可调整逻辑,拿到自定义参数后实现跳转到对应群聊:
@Override
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
    super.onMessageReceived(remoteMessage);
    RemoteMessage.Notification notification=remoteMessage.getNotification();
    // 获取自定义的群ID参数
    String groupId = remoteMessage.getData().get("groupId");
    sendNotification(notification.getTitle(),notification.getBody(), groupId);
}

private void sendNotification(String title,String messageBody, String groupId) {
    // 跳转到对应群聊的ChatActivity,传入群ID
    Intent intent = new Intent(this, ChatActivity.class);
    intent.putExtra("groupId", groupId);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
            PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
    // 原有通知构建逻辑保持不变即可
    String channelId = "1";
    Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    NotificationCompat.Builder notificationBuilder =
            new NotificationCompat.Builder(this, channelId)
                    .setSmallIcon(R.drawable.send_message)
                    .setContentTitle(title)
                    .setContentText(messageBody)
                    .setAutoCancel(true)
                    .setSound(defaultSoundUri)
                    .setContentIntent(pendingIntent);

    NotificationManager notificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel channel = new NotificationChannel(channelId,
                "Channel human readable title",
                NotificationManager.IMPORTANCE_DEFAULT);
        notificationManager.createNotificationChannel(channel);
    }
    // 用群ID哈希值作为通知ID,避免多个群通知互相覆盖
    notificationManager.notify(groupId.hashCode(), notificationBuilder.build());
}

方案2:批量传递群成员注册令牌

如果需要更精细的推送控制(比如部分群成员屏蔽了群通知就不推送),可以用该方案:

  • 从你的服务端拉取当前群组所有未屏蔽通知的成员的FCM注册令牌列表
  • 发送通知时把to参数替换为registration_ids参数,传入令牌数组即可,单请求最多支持传递1000个令牌,超过1000人得分批发送
// 示例替换逻辑
notificationData.put("registration_ids", 你的群成员FCM令牌数组);

重要注意事项

  • 绝对不要在Android客户端硬编码FCM服务端密钥,客户端很容易被反编译拿到密钥,导致被恶意推送甚至滥用FCM配额,正式环境必须把发通知的逻辑全部放到后端实现,客户端只需要调用后端接口传递消息内容和群ID即可
  • 安卓12及以上版本创建PendingIntent需要明确指定FLAG_IMMUTABLE或FLAG_MUTABLE,否则会抛出异常,记得给你的PendingIntent加上对应标记
  • 通知ID建议用群ID的哈希值,避免多个群的通知互相覆盖

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 06:09:03