Skip to content

[Firebase messaging]: Duplicate notifications on android #17330

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 task done
iosephmagno opened this issue May 4, 2025 · 5 comments
Open
1 task done

[Firebase messaging]: Duplicate notifications on android #17330

iosephmagno opened this issue May 4, 2025 · 5 comments
Labels
Needs Attention This issue needs maintainer attention. platform: android Issues / PRs which are specifically for Android. plugin: messaging type: bug Something isn't working

Comments

@iosephmagno
Copy link

Is there an existing issue for this?

  • I have searched the existing issues.

Which plugins are affected?

Messaging

Which platforms are affected?

Android

Description

_firebaseMessagingForegroundHandler and _firebaseMessagingBackgroundHandler are fired multiple times for the same silent (data-only) notification - with firebase messaging 15.2.1

I'm reproducing the issue on android sdk34 Xiaomim Mi Lite 5g, though I saw past similar issues on IOS and this may still affect IOS as well.

Honestly, I’d rather deal with duplicate notifications than miss them entirely.
As a workaround, I’ve implemented a simple cache of notification IDs and filter out duplicates before displaying any alerts. Example code below:

const String kFirebaseNotificationIds = 'firebase_notification_ids';
...

final content = notification.content as ChatNotificationContent;
debugPrint(“DEBUG notification content ${content.id}");
final notificationIds = SharedPrefs.instance.getStringList(kFirebaseNotificationIds) ?? [];
if (notificationIds.contains(content.id)) {
  debugPrint("DEBUG skip duplicate notification");
  return;
} else {
  notificationIds.add(content.id);
  SharedPrefs.instance.setStringList(kFirebaseNotificationIds, notificationIds);
  ...
  // ShowNotification with flutter local notification 
}

...

// Clear kFirebaseNotificationIds when app is paused

I'm still experiencing issues with Android intermittently throttling or blocking push notifications, only to deliver them all at once later. Disabling battery optimizations and enabling autostart improves reliability somewhat, but overall, notification delivery on Android remains inconsistent.

It would be great if the Firebase team could collaborate with the Android team to address this — ideally establishing a more consistent behavior across devices and limiting the ability of manufacturers to interfere with notification delivery mechanisms.

Reproducing the issue

Send a notification to your device.

Firebase Core version

^3.1.1

Flutter Version

3.24.5

Relevant Log Output

Flutter dependencies

Expand Flutter dependencies snippet
Replace this line with the contents of your `flutter pub deps -- --style=compact`.

Additional context and comments

No response

@iosephmagno iosephmagno added Needs Attention This issue needs maintainer attention. type: bug Something isn't working labels May 4, 2025
@iosephmagno
Copy link
Author

iosephmagno commented May 5, 2025

Hi @russellwheatley, in regards to when notifications randomly stop completely when app is in background, can you please let me know if this GPT answer is valid?

It is suggesting that issue is not really the data-only payload, but the DART isolate being killed by the system when app is quit or in background. And it is suggesting to handle notifications with FirebaseMessagingService (native). I start to think it might be right, on IOS we indeed solved the notification issue by handling them natively with the IOS NotificaitonServiceExtension and using Firebase Messaging plugin to still handle the alert onTap. If GPT is right, we could achieve a similar result by handling notification natively on android as well.

GPT:
Why Native FCM Works More Reliably
When using the Flutter Firebase Messaging plugin, you’re relying on:

  • Dart isolate lifecycle
  • Flutter engine startup
  • Plugin initialization order
  • FirebaseMessaging.onBackgroundMessage() logic

This makes notifications less reliable, especially when:

  • The app is terminated or killed
  • The device is in doze mode / battery optimization
  • Manufacturer-specific restrictions exist (e.g. Xiaomi, Oppo)

In contrast, Native Android (Java/Kotlin) FCM integration:

  1. Runs as a system-level Service
  • Your FirebaseMessagingService subclass is registered with Android itself - not Flutter.
  • It runs even if Flutter hasn’t started or the app is killed.
  1. Receives messages via native OS broadcast
  • Android delivers high-priority data-only messages directly to your service, with no Dart engine boot required.
  1. Does not depend on Flutter plugins being ready
  • Dart and plugin init times (or failures) don’t block it.
  • You can decrypt + show the notification instantly from the native thread.

Why This Is Better for Silent + Custom Alerts
With this setup, you:

  • Bypass Dart entirely for notification logic
  • Have full control over decryption, fallback behavior, retry logic, and visual customization
  • Avoid issues from delayed or dropped Dart isolates

But Isn’t It Still a Data Message?
Yes - it’s still a data-only message, but who’s receiving and handling it changes:

  • FirebaseMessagingService (native): Receiver is Android system (Lifecycle Sensitive?: NO)
  • FirebaseMessaging in Dart: Receiver is Dart (Lifecycle Sensitive?: YES)

TL;DR
You’re replacing a Flutter plugin-based receiver with a native Android system service that is more reliable, faster, and doesn’t require Flutter to be alive - perfect for decrypted, silent notifications.

@MichaelVerdon MichaelVerdon added plugin: messaging platform: android Issues / PRs which are specifically for Android. labels May 7, 2025
@russellwheatley
Copy link
Member

russellwheatley commented May 7, 2025

@iosephmagno - I think that chatGPT answer doesn't make sense. We aren't relying on Flutter SDK to receive notifications, and receiving messages isn't reliant on the main Dart isolate running otherwise we wouldn't receive messages. We are using native android API to listen for messages. See receiver: https://github.com/firebase/flutterfire/blob/main/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingReceiver.java

We also need to fire up a background Flutter engine to communicate with your Flutter app otherwise you would never receive the messages in you Flutter background handler.

You are more than welcome to fork off FlutterFire and use it in your own app, you could then post an update with your results if you have found a more reliable alternative. Native updates on android Firebase messaging are few and far between at this point so it wouldn't be that hard to keep it in sync with FlutterFire messaging.

@MichaelVerdon
Copy link
Contributor

Hi there, you definitely had the right idea to disable battery optimisations as that tends to cut off background processes as phone vendors such as Xiaomi like to do this to claim more battery life https://dontkillmyapp.com/xiaomi. I have not been able to reproduce this issue using other android devices, in my case a Pixel and an S21. Unfortunately I don't think we can do anything about it.

@MichaelVerdon MichaelVerdon added blocked: customer-response Waiting for customer response, e.g. more information was requested. and removed Needs Attention This issue needs maintainer attention. labels May 7, 2025
@iosephmagno
Copy link
Author

@russellwheatley - I discovered something that I think might be interesting for you to know. Writing this would take me 30m but I hope it is helpful.

I was going to implement my native service to handle notifications directly on native, without relying on flutter firebase plugins (we do this already on IOS, as it is the only way to handle encrypted non-silent notifications on IOS with instant delivery).

Anyway, while implementing this solution, I discovered something unexpected that allows our app to consistently display silent-notifications on android.

I added a CustomFirebaseMessagingService.

class CustomFirebaseMessagingService : FirebaseMessagingService() {

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        try {
            // Handle the received FCM message here, if needed.
            // val data = remoteMessage.data     

        } catch (e: Exception) {
            Log.e("CustomFirebaseMessagingService", "Exception occurred", e)
            // Optionally handle the exception or rethrow it
        }

    }

}

As you can see I do nothing in onMessageReceived, I was meant to code there the decryption and show alert, but my discovery makes it unnecessary.

I initialize firebase in MainActivity.kt

     // Initialize Firebase
        try {
            FirebaseApp.initializeApp(this)
        } catch (e: Exception) {
            //Log.i("FCM", "Firebase init failed", e)
        }
        FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
            try {
                if (task.isSuccessful) {
                    //Log.i("FCM", "Token: ${task.result}")
                } else {
                    //Log.i("FCM", "Fetching token failed", task.exception)
                }
            } catch (e: Exception) {
                Log.i("FCM", "Exception in token fetch", e)
            }
        }

Here is the Manifest:

<service
            android:name=".CustomFirebaseMessagingService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="false"> <!-- This specifies the service is not exported -->
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>

DISCOVERY
For some reason, the notification is passed to Flutter Firebase Messaging even if I disabled the
onBackgroundMessage in main.dart

  //FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); // disabled
  FirebaseMessaging.onMessage.listen(_firebaseMessagingForegroundHandler); 

KEY POINTS:

  • Unlike flutter firebase messaging plugin, CustomFirebaseMessagingService is always fired and FirebaseMessaging.onBackgroundMessage is always fired as consequence.
  • Tested on a few android devices where silent notifications are not displayed if I rely only on flutter firebase messaging plugin and app is in background for some time (minutes, hrs or days).
  • Even when notifications worked with flutter firebase plugins, they tended to appear slower than with this solution.

My best guess is that being my CustomFirebaseMessagingService a FirebaseMessagingService, it somehow causes that the intercepted notification triggers FirebaseMessaging.onBackgroundMessage.

NOTE:
I also coded a foreground service to keep CustomFirebaseMessagingService always live, but I noticed it is not even required. I think android system wakes up it anyway on fcm notification arrival.

POTENTIAL IMPLICATIONS:

  • If this workaround resolves the issue on your end as well, consider including these steps as a recommended Android setup in your documentation.
  • It only requires a few lines of configuration but could prevent this critical issue for many apps.

@google-oss-bot google-oss-bot added Needs Attention This issue needs maintainer attention. and removed blocked: customer-response Waiting for customer response, e.g. more information was requested. labels May 7, 2025
@iosephmagno
Copy link
Author

as phone vendors such as Xiaomi like to do this to claim more battery life https://dontkillmyapp.com/xiaomi.

Yes, but those limitations aren't the root cause of the Flutter Firebase Messaging issue with notifications. On the same devices, native apps receive notifications and flutter apps don't.
The issue itself is particularly tricky to diagnose due to the many moving parts involved in the cross-platform chain. If we’re lucky to have found something that works, we should thank God and move forward with it.

@russellwheatley this works only on Android. On IOS we had to move to native, as there's no way to make silent-notification quick and reliable on IOS.

Maybe Firebase Messaging might do the same and provide the feature as part of the plugin:

  • create a NotificationExtension (or give to devs a ready skeleton)
  • In didReceive: publish the notification to CFNotificationCenterPostNotification
  • In AppDelegate: trigger Flutter engine when the app is woken by NotificationServiceExtension
  • In main.dart: instruct devs to add AppWakeup which will be fired and receive the notification payload from native.
class AppWakeup {
 static const MethodChannel _channel =
 MethodChannel('com.company.app/wakeup_channel'); // Match Swift channel name from AppDelegate.swift and NotificationService.swift
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Attention This issue needs maintainer attention. platform: android Issues / PRs which are specifically for Android. plugin: messaging type: bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants