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

Android会话通知更新:标准实现方式及最佳实践咨询

问题

我们在Android应用中使用NotificationCompat.MessagingStyle实现聊天消息推送通知,通过OneSignal SDK接收通知,并实现INotificationServiceExtension对通知进行修改。需求是收到新消息通知时,将新消息追加到同一会话的已有通知中(无则新建)。

当前实现方式是通过locusId查找旧通知、复制其样式、取消旧通知后再创建包含新消息的新通知,但不确定该方式是否正确。此外,通知ID由OneSignal自动生成,未进行追踪。简化实现代码如下:

class PushNotificationService : INotificationServiceExtension {
    override fun onNotificationReceived(event: INotificationReceivedEvent) {
        when (val type = event.notification.additionalData?.getString("type")) {
            "new_message" -> {
                // Can't be null if type exists
                val data: NewMessagePushData = event.notification.additionalData!!.let {
                    Json.decodeFromString(it.toString())
                }
                event.applyConversationStyle(data)
            }
            // ...
        }
    }

    private fun INotificationReceivedEvent.applyConversationStyle(
        data: NewMessagePushData
    ) {
        val notificationManager = context.getSystemService(FirebaseMessagingService.NOTIFICATION_SERVICE)
                as NotificationManager
        val previousNotification = notificationManager
            .activeNotifications
            .find {
                it.notification.locusId?.id == data.chatId.toString()
            }
        val usersDao = getService(context).usersDao()
        notification.setExtender { notification ->
            val (user, icon) = runBlocking {
                val user = /* Fetch user from local DB*/
                val icon = user?.let {/*Fetch user's avatar and process it for display*/}
                user to icon
            }
            if (user == null) return@setExtender notification

            val person = /*Build an androidx.core.app.Person from user data*/

            val style = previousNotification?.let {
                NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(it.notification)
            } ?: NotificationCompat.MessagingStyle(
                /*Build an androidx.core.app.Person for current user*/
            )
            
            // This is questionable!
            previousNotification?.id?.let {
                OneSignal.Notifications.removeNotification(it)
            }


            notification
                .setContentTitle(user.fullName.asString(context))
                .setStyle(
                    style
                        .addMessage(
                            NotificationCompat.MessagingStyle.Message(
                                data.body,
                                System.currentTimeMillis(),
                                person
                            )
                        )
                )
                .addPerson(person)
                .setLargeIcon(icon?.toIcon(context))
                .setCategory(Notification.CATEGORY_MESSAGE)
                .setLocusId(LocusIdCompat(data.chatId.toString()))
                .setShortcutInfo(context.createConversationShortcutIfNeeded(
                    data = data,
                    user = user,
                    person = person,
                    icon = icon
                ))
        }
    }
}

核心疑问:

  • 正确的标准实现方式是什么?
  • 是否需要引入消息DAO获取消息?
  • 是否应让后端为同一会话固定通知ID,以便系统自动更新通知?还是需要自行追踪这些通知ID?

回答

一、标准实现方式

你的核心聚合思路是对的,但当前先删除旧通知再发送新通知的做法存在问题——会导致通知闪烁,不符合系统推荐的更新逻辑。正确的流程应该是:

  1. 通过locusId或会话ID找到同一会话的旧通知
  2. 从旧通知中提取MessagingStyle,追加新消息
  3. 使用相同的通知ID直接更新旧通知,系统会自动替换并保留会话聚合效果

另外,在INotificationServiceExtension中修改通知时,不需要手动调用OneSignal.Notifications.removeNotification(),只要确保通知ID一致,修改后的通知会自动覆盖旧通知,OneSignal会负责完成后续发送逻辑。

二、是否需要引入消息DAO?

分两种场景判断:

  • 若推送仅包含最新一条消息,且不需要展示完整历史消息链:不需要额外引入DAO,直接从旧通知的MessagingStyle中提取历史消息并追加新消息即可。
  • 若需要在通知中展示最近N条完整消息(比如用户未清除旧通知时,新通知要显示完整上下文):建议引入消息DAO,从本地数据库拉取最近的消息列表重新构建MessagingStyle——这样即使旧通知被清除,新通知也能完整展示历史消息,体验更一致。

三、通知ID的处理方案

这是实现会话聚合的核心,推荐优先级如下:

  1. 后端为同一会话固定通知ID(最优)
    让后端在推送时,给同一会话的所有消息分配同一个通知ID(比如直接用chatId作为通知ID)。系统会自动用新通知替换同ID的旧通知,无需手动查找旧通知,MessagingStyle的消息聚合会自动生效,代码更简洁。

    • 注意:需要在OneSignal推送配置中指定固定的通知ID(具体可参考OneSignal推送API的notificationId或android_group参数),覆盖其自动生成逻辑。
  2. 本地自行追踪通知ID
    如果后端无法修改,可在本地维护chatId与通知ID的映射表(用SharedPreferences或本地数据库存储)。收到新消息时:

    • 先查映射表拿到对应会话的通知ID,用该ID更新通知
    • 若没有映射记录,则使用OneSignal自动生成的ID,并将chatId与该ID的映射关系保存下来
    • 需要额外处理用户手动清除通知的情况(此时要删除对应映射记录),复杂度稍高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 21:14:50