跳转到主要内容
重复通知是指同一设备多次收到相同内容的消息。本指南涵盖最常见的原因及解决方法。 如果同一用户在多台设备(手机、平板、桌面)上看到通知,这是基于您的定向配置(细分群组、外部 ID 等)的预期行为。如果您遇到重复的应用内消息,请参阅应用内消息故障排除指南。
Apple 确认了 iOS 17 中导致重复的错误。此问题已在 iOS 17.3 中修复。了解更多

从这里开始

选择最符合您情况的部分:

多次发送相同消息

重复通知的最常见原因是通过 OneSignal API 多次发送相同的通知载荷。常见原因:
  • 您的服务器重试请求而不检查第一次是否成功。
  • 后端管道中的逻辑重复发送了同一通知。
  • 您正在迁移到 OneSignal 但仍从之前的提供商发送通知。避免同时从两个系统发送。

幂等 API 请求

通过在重试时复用幂等键来防止重复消息。

第三方通知处理程序

当应用中的其他代码处理 OneSignal 的推送载荷并在 OneSignal 之外额外显示通知时,就会出现重复。包括:
  • 其他接收相同载荷的远程推送 SDK(例如 Firebase Cloud Messaging)。
  • 自定义消息处理程序,例如 Android 上的 FirebaseMessagingService 或 iOS 上的 UNUserNotificationCenterDelegate,它们读取传入载荷并从中构建本地通知。
当 OneSignal SDK 是唯一的处理程序时,会自动过滤 OneSignal 载荷。第三方代码不知道要过滤它们,除非您告诉它。

识别 OneSignal 通知

每条 OneSignal 通知都包含一个带有 i 键的 custom 对象,其中包含 OneSignal 通知 UUID:
JSON
{
  "custom": {
    "i": "the-notification-id"
  }
}
有关完整的载荷参考,请参阅自定义 OneSignal 载荷结构

从第三方处理程序中过滤 OneSignal 通知

在任何非 OneSignal 的处理程序中,检查 custom.i 键,并在其存在时提前返回。OneSignal 随后处理自己的载荷,而您的其他代码继续处理自己的载荷。
在自定义 FirebaseMessagingService 或其他远程消息处理程序中:
Kotlin
override fun onMessageReceived(remoteMessage: RemoteMessage) {
    val customJson = remoteMessage.data["custom"]
    if (customJson != null) {
        try {
            val custom = JSONObject(customJson)
            if (custom.has("i")) {
                // OneSignal 通知——让 OneSignal SDK 处理它
                return
            }
        } catch (e: JSONException) {
            // 不是 OneSignal 载荷,继续执行
        }
    }

    // 在这里处理您自己的通知
}
如果您控制从其他提供商发送的载荷结构,请添加一个独特的标记键(例如 my_app_notification: true),并在两个方向上进行过滤——在您自己的代码中只处理包含您标记的通知,让 OneSignal 处理包含 custom.i 键的通知。

Android 通知处理程序

OneSignal 提供了两个 Android 回调,允许您在通知显示之前拦截和自定义: 当这些回调除了 OneSignal 的自动显示之外还显示通知时,就会出现重复。

preventDefault()display() 规则

OneSignal 会自动显示每条通知,除非您通过 event.preventDefault() 抑制它。两个常见错误会导致重复:
  • 在未首先调用 event.preventDefault() 的情况下调用 event.getNotification().display()——会导致通知显示两次。
  • 在 OneSignal 也显示原始通知时,使用 NotificationManagerCompat.notify() 另外发布一条通知。
只能有一条显示路径是活动的——要么是 OneSignal 的 display(),要么是您自己的 NotificationManagerCompat.notify(),不能同时使用两者。
override fun onNotificationReceived(event: INotificationReceivedEvent) {
    event.preventDefault()

    val notification = event.notification
    notification.setExtender { builder ->
        builder.setColor(0xFF0000FF.toInt())
    }
    notification.display()
}
同样的规则适用于前台生命周期监听器中的 onWillDisplay
Kotlin
OneSignal.Notifications.addForegroundLifecycleListener(object : INotificationLifecycleListener {
    override fun onWillDisplay(event: INotificationWillDisplayEvent) {
        event.preventDefault()

        // 渲染您自己的 UI 或调用 event.notification.display() 显示 OneSignal 通知
    }
})
如果您调用了 preventDefault() 但从未调用 display(),通知将被静默丢弃,用户不会看到它。确保每条代码路径要么恰好调用一次 display(),要么有意抑制通知。
如果您同时使用通知服务扩展和前台生命周期监听器,请确保对于给定的应用状态只有一个处理程序显示通知。从两者都调用 display() 会产生重复。

回调中的异步工作

当您在显示之前执行后台工作(网络调用、数据库查找)时,请在回调中同步调用 preventDefault(),并仅在异步工作完成后调用 display()。在不调用 preventDefault() 的情况下从回调返回会让 OneSignal 显示通知,之后再调用 display() 则会产生重复。

iOS 前台处理程序

OneSignal 在 SDK 初始化期间将自身设置为 UNUserNotificationCenterDelegate。如果您的应用也实现了前台通知处理,通知可能会显示两次——一次来自 OneSignal,一次来自您的代码。 导致重复的常见模式:
  • 实现了自己的 UNUserNotificationCenterDelegate 而不将调用转发给 OneSignal,然后在同一方法中调用 completionHandler([.banner, .sound]) 并显示自定义的应用内警报。
  • 在 OneSignal 也在显示传入推送的同时调度本地通知(UNUserNotificationCenter.current().add(...))。
要在不导致重复的情况下控制前台显示,请使用 OneSignal 的前台生命周期监听器,并在您想自己渲染通知时调用 event.preventDefault()
Swift
OneSignal.Notifications.addForegroundLifecycleListener(self)

func onWillDisplay(event: OSNotificationWillDisplayEvent) {
    event.preventDefault()

    // 渲染您自己的 UI 或调用 event.notification.display() 显示 OneSignal 通知
}

多个应用实例

当您同时安装了应用的生产版本和开发版本时,可能会出现重复。每个应用都有唯一的包名并接收自己的推送令牌。长按通知以确认是哪个应用实例发送的。

诊断提示

为了更快地调试重复问题,请收集以下信息并发送给 OneSignal 支持:
  • 收到重复通知的设备的 OneSignal 订阅 ID
  • OneSignal 消息 ID 或仪表板中消息的链接
  • 应用中其他库或插件的列表
  • 重现问题的调试日志
  • 详细的复现步骤

常见问题

如果我的应用中有两个通知 SDK 会怎样?

两个 SDK 可能会独立处理并显示同一通知。OneSignal 会自动过滤自己的通知,但其他 SDK 不会。通过检查 custom.i 键,从其他 SDK 的处理程序中过滤 OneSignal 载荷。有关代码示例,请参阅第三方通知处理程序

如何同时从之前的提供商和 OneSignal 发送推送?

您可以逐步过渡,但要避免从两个提供商发送相同的消息。
  • Android:在集成 OneSignal 并发布应用时删除旧 SDK 通知处理代码。随着用户更新,他们将停止接收来自旧提供商的推送。
  • iOS:在用户更新期间,您可以暂时继续从旧提供商发送。完全过渡后,仅从 OneSignal 发送以避免重复。

如何防止快速变化更新的多条通知?

创建通知 API 中使用 collapse_id 来替换之前的通知而不是堆叠它们。当多条通知共享相同的 collapse_id 时,每条新通知都会替换通知栏中的上一条。这对于股票价格更新、实时比分、配送预计到达时间和类似的频繁更新非常有用。

相关页面

移动 SDK 故障排除

解决 iOS 和 Android 上推送通知投递的常见问题。

Web 推送故障排除

调试 Web 推送订阅、Service Worker 和投递问题。

应用内消息故障排除

修复应用内消息显示、触发器和重复问题。

移动服务扩展

在 iOS 和 Android 上显示之前拦截和自定义推送通知。

未显示的通知(Web)

对已发送但未显示的 Web 推送通知进行故障排除。