如何利用Firebase Cloud Messaging实现Android应用中特定用户向另一特定用户推送通知
实现Android应用特定用户间FCM推送的完整指南
嘿,要搞定特定用户之间用FCM发推送,核心逻辑其实就是把用户ID和他们的FCM设备令牌绑定起来,然后通过令牌精准定位推送目标。我给你一步步拆解整个流程,都是实战里常用的方案:
一、先做好基础配置
首先得确保你的Android项目已经集成了FCM:
- 去Firebase控制台创建项目,添加你的Android应用,下载
google-services.json放到项目的app目录下。 - 在
build.gradle里添加FCM依赖(用最新版本就行):
implementation 'com.google.firebase:firebase-messaging:23.4.1'
接下来,每个用户登录后必须获取他的FCM设备令牌——这是推送的唯一标识:
FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> if (!task.isSuccessful) { Log.w("FCM_TOKEN", "获取令牌失败", task.exception) return@addOnCompleteListener } // 拿到当前设备的FCM令牌 val fcmToken = task.result // 关键!把这个令牌和当前登录用户的ID绑定,上传到你的后端数据库 // 比如调用你的API:POST /api/user/fcm-token,参数是userId和fcmToken }
注意:FCM令牌可能会变化(比如用户卸载重装、换设备、清数据),所以要监听令牌更新,及时同步到后端:
class MyMessagingService : FirebaseMessagingService() { override fun onNewToken(token: String) { super.onNewToken(token) // 同样把新令牌上传到后端,替换该用户旧的令牌记录 } }
别忘了在AndroidManifest.xml里注册这个服务:
<service android:name=".MyMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
二、核心:维护用户与令牌的映射关系
你的后端需要专门存一个用户ID → FCM令牌列表的映射(用MySQL、Firestore、MongoDB都行):
- 用户登录或令牌更新时,同步更新这个映射;
- 如果一个用户有多个设备(比如手机+平板),就存多个令牌,这样发通知时能推送到所有设备;
- 后续发送通知时,后端先根据接收方的用户ID,从数据库里取出对应的FCM令牌。
三、发送通知的两种方案(推荐后端处理)
方案1:后端调用FCM API发送(最安全、推荐)
直接在客户端发会暴露FCM的服务器密钥,风险很高,所以最好是客户端触发请求到你的后端,后端再调用FCM的API发送。
用Firebase Admin SDK(后端更省心)
比如Java后端的示例:
// 先初始化Admin SDK(提前在Firebase控制台下载服务账号密钥) FirebaseOptions options = new FirebaseOptions.Builder() .setCredentials(GoogleCredentials.fromStream(new FileInputStream("service-account-key.json"))) .build(); FirebaseApp.initializeApp(options); // 构建推送消息 Message message = Message.builder() .setToken(targetUserFcmToken) // 从数据库拿到的接收方令牌 .setNotification(Notification.builder() .setTitle("来自小李的消息") .setBody("周末要不要一起去爬山?") .build()) .putData("senderId", "小李的用户ID") // 自定义数据,用来后续处理 .putData("chatId", "xxx") .build(); // 发送消息 String response = FirebaseMessaging.getInstance().send(message); System.out.println("推送成功:" + response);
用FCM HTTP API(适合没有用Admin SDK的场景)
用curl示例:
curl -X POST "https://fcm.googleapis.com/fcm/send" \ -H "Authorization: key=你的FCM服务器密钥" \ -H "Content-Type: application/json" \ -d '{ "to": "接收方的FCM令牌", "notification": { "title": "来自小李的消息", "body": "周末要不要一起去爬山?" }, "data": { "senderId": "小李的用户ID", "chatId": "xxx" } }'
客户端的操作很简单:当用户要给另一个用户发通知时,调用你的后端API,传入接收方用户ID、通知标题、内容等参数就行,剩下的交给后端处理。
方案2:客户端直接发送(不推荐,不安全)
如果非要在客户端发,也可以用FCM的sendToToken方法,但强烈不建议——因为需要配置FCM的服务器密钥,一旦泄露,别人就能冒充你的应用发推送。示例代码:
val message = RemoteMessage.Builder(targetUserFcmToken) .setNotification(RemoteMessage.Notification.Builder() .setTitle("来自小李的消息") .setBody("周末要不要一起去爬山?") .build()) .addData("senderId", "小李的用户ID") .build() FirebaseMessaging.getInstance().send(message)
四、接收并处理通知
当目标用户的设备收到推送时,如果你要自定义通知样式或者处理点击跳转,得在MyMessagingService里重写onMessageReceived方法:
override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) // 处理通知内容 remoteMessage.notification?.let { notification -> val title = notification.title val content = notification.body // 显示自定义通知 showCustomNotification(title, content, remoteMessage.data) } // 处理自定义data数据(即使应用在后台,data也会传到这里) val senderId = remoteMessage.data["senderId"] val chatId = remoteMessage.data["chatId"] // 可以根据这些数据做逻辑,比如记录未读消息 } private fun showCustomNotification(title: String?, content: String?, data: Map<String, String>) { // 点击通知后跳转到聊天页面 val intent = Intent(this, ChatActivity::class.java) intent.putExtra("senderId", data["senderId"]) intent.putExtra("chatId", data["chatId"]) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) // 构建通知 val notificationBuilder = NotificationCompat.Builder(this, "PRIVATE_CHAT_CHANNEL") .setSmallIcon(R.drawable.ic_chat_notification) .setContentTitle(title) .setContentText(content) .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent) .setAutoCancel(true) // Android 8.0+需要创建通知渠道 val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( "PRIVATE_CHAT_CHANNEL", "私人聊天通知", NotificationManager.IMPORTANCE_HIGH ) notificationManager.createNotificationChannel(channel) } // 显示通知 notificationManager.notify(System.currentTimeMillis().toInt(), notificationBuilder.build()) }
五、一些避坑提醒
- 令牌失效处理:FCM会返回失效令牌的错误,后端要及时把这些令牌从数据库里删掉,避免无效请求;
- 后台通知处理:如果应用在后台,系统会自动显示通知,点击后才会把data数据传到启动的Activity,所以要在Activity的
onCreate或onNewIntent里处理data; - Android 13+权限:需要申请
POST_NOTIFICATIONS权限,记得在Manifest里添加,并且运行时请求; - 安全性:FCM的服务器密钥和Admin SDK的服务账号密钥绝对不能暴露给客户端,只能放在后端。
内容的提问来源于stack exchange,提问作者Sã¡t Khã¡lã¯fã¡




