Firebase Messaging (FCM) OnMessageReceived Running Twice

KylukeMcDougallKylukeMcDougall ✭✭USMember ✭✭

Good day

I am using Firebase Messaging to send/Receive Push Notification on Android.

I've got the following implementation which is triggering OnMessageReceived twice on Android v 8.0, after rebooting the phone once, opening the app, rebooting the phone again and then sending myself a push notification.

Any ideas on what is causing this? I've looked online and found suggestions on removing any old GCM references which I have done already. The first time the app is built and deployed to the device, it doesn't happen and all subsequent push notifications seem fine. This just happens when the phone goes through the above mentioned reboot procedure. It may happen in other cases too but this has been my method to re-create the issue.

The App is compiled using

  • Xamarin.Forms 3.4.0.1008975
  • Xamarin.Firebase.Messaging 60.1142.1

If any further information is required, please ask me.

The code is as follows:

MainActivity.cs

using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Gms.Common;
using Android.OS;
using Android.Views;
using Plugin.Permissions;
using Android.Util;
using Android.Gms.Common.Apis;
using Android.Gms.Drive;
using Android.Runtime;
using System;

namespace App.Droid
{
    [Activity(Label = "App", Icon = "@drawable/launch_icon", LaunchMode = LaunchMode.SingleTop, Theme = "@style/MyTheme", ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]

    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, GoogleApiClient.IConnectionCallbacks, GoogleApiClient.IOnConnectionFailedListener, IResultCallback
    {
        const string TAG = "MainActivity";
        const int REQUEST_CODE_RESOLUTION = 3;
        public static GoogleApiClient googleApiClient;
        public static byte CurrentGoogleDriveState = 0x00;

        internal static readonly string CHANNEL_ID = "default";
        internal static readonly int NOTIFICATION_ID = 100;

        protected override void OnCreate(Bundle bundle)
        {

            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);
                LoadApplication(new App("", ""));

            IsPlayServicesAvailable();
            CreateNotificationChannel();
        }

        void CreateNotificationChannel()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.O)
            {
                // Notification channels are new in API 26 (and not a part of the
                // support library). There is no need to create a notification
                // channel on older versions of Android.
                return;
            }

            var channel = new NotificationChannel(CHANNEL_ID,
                                                  "FCM Notifications",
                                                  NotificationImportance.Default)
            {

                Description = "Firebase Cloud Messages appear in this channel"
            };

            var notificationManager = (NotificationManager)GetSystemService(Android.Content.Context.NotificationService);
            notificationManager.CreateNotificationChannel(channel);
        }

        public bool IsPlayServicesAvailable()
        {
            int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
            if (resultCode != ConnectionResult.Success)
            {
                return false;
            }
            return true;
        }
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto" package="--Redacted--" android:versionCode="--Redacted--" android:versionName="--Redacted--">
    <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="26" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.FLASHLIGHT" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-feature android:name="android.hardware.screen.landscape" />
    <uses-feature android:name="android.hardware.wifi" android:required="false" />
    <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
    <application android:label="--Redacted--" android:icon="@drawable/logo_cropped" android:allowBackup="true" android:largeHeap="true">
        <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
        <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="${applicationId}" />
            </intent-filter>
        </receiver>
        <meta-data android:name="android.max_aspect" android:value="2.1" />
    </application>
</manifest>

FirebaseRegistration.cs

using Android.App;
using Firebase.Iid;
using Android.Util;
using System;
using System.IO;
using Android.Content;

namespace App.Droid
{
    [Service]
    [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
    public class MyFirebaseIIDService : FirebaseInstanceIdService
    {
        const string TAG = "MyFirebaseIIDService";
        public override void OnTokenRefresh()
        {
            base.OnTokenRefresh();

            var refreshedToken = FirebaseInstanceId.Instance.Token;
            Log.Debug(TAG, "Refreshed token: " + refreshedToken);
            SendRegistrationToServer(refreshedToken);

        }

        /// <summary>
        /// Sends the token to server.
        /// </summary>
        /// <param name="token">Token.</param>
        void SendRegistrationToServer(string token)
        {
            // My code to send the push notification ID to our backend
        }
    }
}

FirebaseMessaging.cs

using System;
using System.Globalization;
using Android.App;
using Android.Content;
using System.Linq;
using Firebase.Messaging;
using System.Collections.Generic;
using System.Threading.Tasks;
using Android.OS;
using static Android.App.ActivityManager;
using Android.Support.V4.App;

namespace App.Droid
{
    [Service]
    [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
    public class MyFirebaseMessagingService : FirebaseMessagingService
    {
        const string TAG = "MyFirebaseMsgService";

        async public override void OnMessageReceived(RemoteMessage message)
        {
            base.OnMessageReceived(message);

            Console.WriteLine("["+this.GetType().FullName+"]\t\t\t\t Push Notification Received! \n\n" + Newtonsoft.Json.JsonConvert.SerializeObject(message));
        }
    }
}

Answers

  • KylukeMcDougallKylukeMcDougall ✭✭ USMember ✭✭

    Hi @YorkGo, thank you for the link. I've already looked into that and don't see any references to GCM services that could be being called.

  • KylukeMcDougallKylukeMcDougall ✭✭ USMember ✭✭

    Bumping this.

  • KylukeMcDougallKylukeMcDougall ✭✭ USMember ✭✭

    Bump

  • KylukeMcDougallKylukeMcDougall ✭✭ USMember ✭✭

    bump

  • chendanouchendanou Member

    I have the same issue, have you found solution ?

  • edgallardo_absedgallardo_abs Member

    Hello @KylukeMcDougall we are encountering this issue with the same exact scenario to reproduce. Have you gotten an answer/solution for this?

  • KylukeMcDougallKylukeMcDougall ✭✭ USMember ✭✭

    Hi guys, sorry, been busy.

    My solution is somewhat simple. To work around the issue i've created a List<string> that gets filled with a unique ID (from the backend). Once a push notification is received by the device, it adds the ID to the List<string> and removes it once it's done with the processing.

    Each notification will first check to see if its ID exists in the List<string>. If it does, it means that the same push notification is currently being processed by the code and it just returns early (therefore not process it twice).

    It doesn't need to store anything to disk and the List<string> only needs to live as long as it's processing a push notification.

Sign In or Register to comment.