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

FCM主题推送引发Firestore通知重复问题求助

动物领养APP FCM通知重复写入Firestore问题

问题背景

我和学生开发一款动物领养APP,未修改FCM相关代码的情况下出现突发Bug:新增动物后,Firestore通知集合中出现重复文档。

复现步骤

  1. 发送FCM推送:向Firestore添加新Animal时,通过POST发送FCM主题推送(无自定义后端,教学场景),此步骤正常,通知可在MyFirebaseMessagingService中接收。
  2. 处理通知并写入Firestore:重写onMessageReceived处理通知,将通知数据存入Firestore集合(限制最多5条)以展示通知历史。

错误现象

  • 预期:通知历史页面仅显示1条最新通知
  • 实际:Firestore集合中出现两个timestamp完全相同的文档,一个tipoAnimal为"Perro/Gato",另一个为空字符串;调试时onMessageReceived仅进入一次,仅收到一条推送通知。

代码片段

1. FCM推送发送代码

private fun sendNotificationToServer(animal: Animal, animalId: String) {
    val retrofit = Retrofit.Builder()
        .baseUrl("https://fcm.googleapis.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    val service = retrofit.create(FCMService::class.java)

    val notification = FCMNotification(
        to = "/topics/animales",
        notification = null,
        data = FCMAnimalData(
            idAnimal = animalId,
            tipoAnimal = animal.tipoAnimal.animalString,
            base64Image = "",
            title = "Nuevo animal",
            body = "Se ha añadido un nuevo animal a la lista. Haz clic para conocerlo!",
            timestamp = Timestamp.now().seconds
        )
    )

    val call = service.sendNotification(notification)

    call.enqueue(object : Callback<ResponseBody> {
        override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
            if (response.isSuccessful) {
                Log.d("NOTIFICATION POST SUCCESS", response.message())
            } else {
                Log.d("NOTIFICATION POST FAILURE1", response.message())
            }
        }

        override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
            Log.d("NOTIFICATION POST FAILURE2", t.message.toString())
        }
    })
}

2. FCM消息处理代码

val fcmData = remoteMessage.data
sendNotification(fcmData["title"] as String, fcmData["body"] as String)
firestore.collection(ALERTS_TABLE_NAME)
    .add(FCMAnimalData(
        base64Image = fcmData["base64Image"] as String,
        idAnimal = fcmData["idAnimal"] as String,
        tipoAnimal = fcmData["tipoAnimal"] as String,
        title = fcmData["title"] as String,
        body = fcmData["body"] as String,
        timestamp = fcmData["timestamp"]?.toLong() as Long
    )).addOnSuccessListener {
        Log.d("NOTIFICATION", "ADDED")
        firestore.collection(ALERTS_TABLE_NAME).get().addOnSuccessListener { notificationList ->
            if (notificationList.size() > 5) {
                firestore.collection(ALERTS_TABLE_NAME).document(notificationList.first().id).delete()
            }
        }
    }

3. Animal数据类与枚举定义

@Parcelize
data class Animal(
    @DocumentId var id: String = "",
    val nombre: String = "",
    val edad: Int = 0,
    val imagenBase64: String = "",
    val raza: String = "",
    val descripcion: String = "",
    var favorito: Boolean = false,
    val tipoAnimal: TipoAnimal = TipoAnimal.PERRO,
    val procedencia: TipoUsuario = TipoUsuario.PARTICULAR,
    val idUsuario: String = "",
    val timestamp: Long = 0
): Parcelable

enum class TipoAnimal(val animalString: String) {
    PERRO("Perro"),
    GATO("Gato")
}

可能原因分析与排查建议

1. 推送发送逻辑被重复触发

虽然调试时onMessageReceived仅执行一次,但sendNotificationToServer可能被调用了两次:

  • 检查新增Animal到Firestore的代码逻辑,确认sendNotificationToServer是否在addOnSuccessListener中被重复绑定或调用。
  • 在sendNotificationToServer函数开头添加日志,打印animal.tipoAnimal.animalString和调用次数,验证是否发送了两次不同的推送。

2. FCM数据字段解析的空值安全问题

代码中使用as String强制转型,若remoteMessage.data中字段为null可能引发异常,此处出现空字符串需排查:

  • 在onMessageReceived中打印remoteMessage.data的完整内容(Log.d("FCM_DATA", fcmData.toString())),确认是否存在重复字段或空值。
  • 替换强制转型为安全取值,比如fcmData["tipoAnimal"] ?: "",避免潜在空值问题,同时明确空值来源。

3. 枚举序列化/反序列化异常

若Animal对象的tipoAnimal字段未正确初始化,会导致推送时发送空字符串:

  • 检查新增Animal时的逻辑,确认tipoAnimal是否被正确赋值(比如表单提交时是否选择了动物类型)。
  • 验证从Firestore读取Animal对象时,枚举TipoAnimal的反序列化是否正常(是否存在字段名不匹配或Gson配置问题)。

4. FCM重复投递(低概率)

FCM存在极端情况下的重复投递机制,即使客户端仅收到一次回调,也需验证:

  • 在onMessageReceived中打印remoteMessage.messageId,确认两次写入是否对应同一条消息ID。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 01:12:13