Skip to content

fix: clear unprocessedOpenedNotifs queue after replaying to new listener#2632

Merged
nan-li merged 1 commit intomainfrom
fix/clear_processed_notification_clicks
Apr 30, 2026
Merged

fix: clear unprocessedOpenedNotifs queue after replaying to new listener#2632
nan-li merged 1 commit intomainfrom
fix/clear_processed_notification_clicks

Conversation

@nan-li
Copy link
Copy Markdown
Contributor

@nan-li nan-li commented Apr 30, 2026

Description

One Line Summary

Clear unprocessedOpenedNotifs after replaying so subsequent listener additions don't refire old click events.

Details

Motivation

The unprocessedOpenedNotifs queue exists to buffer notification click events that arrive before any INotificationClickListener is registered (e.g. when the app is opened by tapping a notification and the listener is registered later in the app's startup sequence). When a listener is added, the queue is replayed via extOpenedCallback.fireOnMain, which fires to all subscribers.

The bug: the queue was never cleared after replaying. This caused two problems:

  1. Adding a second listener at any point would refire all old, already-delivered events to all existing listeners — the first listener would receive duplicate onClick callbacks for events it already handled.
  2. The queue grows unboundedly over the lifetime of the SDK instance, since events are never removed.

Adding multiple click listeners is officially supported by the SDK (the subscriber list is a MutableList), and the Flutter wrapper SDK also relies on remove + re-add of listeners during plugin lifecycle, both of which trip the bug.

Scope

  • Adds unprocessedOpenedNotifs.clear() after the replay loop in NotificationLifecycleService.addExternalClickListener.
  • No changes to public API.
  • Once a queued event is delivered to the current set of subscribers, it is considered "processed" and is no longer eligible for replay to future listeners.

Alternatives considered

  1. Fire only to the newly added listener instead of all subscribers. This is more semantically correct — existing subscribers have already received the events, so only the newly added subscriber needs the replay. This was not chosen because EventProducer does not currently expose a "fire to single subscriber" method, and adding one would be a broader change. The simpler clear() after replay achieves the same end-state (existing listeners only receive events once, late-added listeners do not receive already-delivered events) with a minimal diff.
  2. Track per-subscriber delivery state. Significantly more complex, and not needed given the current semantics of the queue (it is a buffer for cold-start, not a replay log).

Testing

Unit testing

Added queued click events are not refired when a second listener is added in NotificationLifecycleServiceTests:

  • Queues a click event before any listener is registered (via notificationOpened with no subscribers).
  • Adds a first listener and verifies it fires exactly once.
  • Adds a second listener and verifies neither listener fires again — the queue was cleared after the first replay.

Verified the test catches the bug by reverting the production change and confirming the test fails.

Manual testing

Reproduced via the OneSignal Flutter SDK example app on Android emulator with multiple back-press → notification-click cycles, confirming that without this fix old click events accumulate and refire on every subsequent listener registration, and that the fix prevents duplicate fires.

Affected code checklist

  • Notifications
    • Display
    • Open
    • Push Processing
    • Confirm Deliveries
  • Outcomes
  • Sessions
  • In-App Messaging
  • REST API requests
  • Public API changes

Checklist

Overview

  • I have filled out all REQUIRED sections above
  • PR does one thing
  • Any Public API changes are explained in the PR details and conform to existing APIs

Testing

  • I have included test coverage for these changes, or explained why they are not needed
  • All automated tests pass, or I explained why that is not possible
  • I have personally tested this on my device, or explained why that is not possible

The queue of click events that arrive before any listener is registered
was never cleared after being replayed. Each subsequent addClickListener
call would refire those old events to all existing subscribers, causing
duplicate onClick invocations on already-registered listeners.
@nan-li nan-li requested review from a team, abdulraqeeb33, fadi-george and sherwinski and removed request for sherwinski April 30, 2026 16:40
@nan-li nan-li merged commit 2ce9d99 into main Apr 30, 2026
6 of 8 checks passed
@nan-li nan-li deleted the fix/clear_processed_notification_clicks branch April 30, 2026 22:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants