You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何利用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的onCreateonNewIntent里处理data;
  • Android 13+权限:需要申请POST_NOTIFICATIONS权限,记得在Manifest里添加,并且运行时请求;
  • 安全性:FCM的服务器密钥和Admin SDK的服务账号密钥绝对不能暴露给客户端,只能放在后端。

内容的提问来源于stack exchange,提问作者Sã¡t Khã¡lã¯fã¡

火山引擎 最新活动