类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
相关产品推荐
相关产品推荐

