简体   繁体   中英

FCM - Android Xamarin NotRegistered error when sending message after re-debugging the App

I am developing an App in Xamarin Android, for notifications I am using FCM the Pre-Release package: https://www.nuget.org/packages/Xamarin.Firebase.Messaging/

Now everything works fine if I clean the App data, the OnTokenRefresh event is fired and a new token is generated - when I send a new notification on this Token the notification is sent and received by the device in OnMessageReceived() -

The problem is when I make changes to the code and run the application again, if I use the old token I get the NotRegistered Error when sending a notification, but if I go and clean the App Data, then the OnTokenRefresh() is fired a new token is generated - the new token works.

Similar issue here, but this is GCM (I am using FCM):

Google cloud message 'Not Registered' failure and unsubscribe best practices?

https://stackoverflow.com/a/36856867/1910735

https://forums.xamarin.com/discussion/65205/google-cloud-messaging-issues#latest

My FCMInstanceIdService

[Service, IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class FCMInstanceIdService : FirebaseInstanceIdService
{
    private string Tag = "FCMInstanceIdService";

    public override void OnTokenRefresh()
    {
        var fcmDeviceId = FirebaseInstanceId.Instance.Token;

        if (Settings.DeviceId != fcmDeviceId)
        {
            var oldDeviceId = Settings.DeviceId;

            Settings.DeviceId = fcmDeviceId;

            //TODO: update token on DB - Currently OnTokenRefresh is only called when: 1. App data is cleaned, 2. The app is re-installed
            //_usersProvider.UpdateUserDeviceId(oldDeviceId, fcmDeviceId);
        }

        base.OnTokenRefresh();
    }
}

My Message Receive Service:

[Service, IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FCMListenerService : FirebaseMessagingService
{
    private string Tag = "FCM_Listener_Service";

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

        var notification = message.GetNotification();
        var data = message.Data;
        var title = notification.Title;
        var body = notification.Body;

        SendNotification(title, body);
    }

    private void SendNotification(string title, string body)
    {
        //TODO: Display notification to user
    }
}

Manifest:

<application android:label="TBApp" android:theme="@style/TBAppTheme">

<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="com.google.android.c2dm.intent.RECEIVE" />
    <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
    <category android:name="${applicationId}" />
  </intent-filter>
</receiver>
</application>

How do I force refresh the FCM Token in DEBUG mode so I don't have to delete the App Data every time I run the application?

As this issue only happens when running the Application from Visual Studio while debugging the application (not in the version deployed to PlayStore), what I did to solve the issue temporarily is I created the following service:

    [Service]
    public class FCMRegistrationService : IntentService
    {
        private const string Tag = "FCMRegistrationService";
        static object locker = new object();

        protected override void OnHandleIntent(Intent intent)
        {
            try
            {
                lock (locker)
                {
                    var instanceId = FirebaseInstanceId.Instance;
                    var token = instanceId.Token;

                    if (string.IsNullOrEmpty(token))
                        return;

#if DEBUG
                    instanceId.DeleteToken(token, "");
                    instanceId.DeleteInstanceId();

#endif


                }
            }
            catch (Exception e)
            {
                Log.Debug(Tag, e.Message);
            }
        }
    }

then in my launch Activity (the activity that loads whenever the Application is opened is do the following:

protected override void OnCreate(Bundle savedInstanceState)
{
            base.OnCreate(savedInstanceState);


#if DEBUG
            if (!IsMyServiceRunning("FCMRegistrationService"))
            {
                var intent = new Intent(this, typeof(FCMRegistrationService));
                StartService(intent);
            }

            // For debug mode only - will accept the HTTPS certificate of Test/Dev server, as the HTTPS certificate is invalid /not trusted
            ServicePointManager.ServerCertificateValidationCallback += (o, certificate, chain, errors) => true;
#endif

}

This will unregister your existing FCMToken and will refresh the Token, so the OnTokenRefresh method will be called, then you will have to write some logic to update the FCMToken on the server.

[Service, IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class FCMInstanceIdService : FirebaseInstanceIdService
{
   // private string LogTag = "FCMInstanceIdService";

    public override void OnTokenRefresh()
    {
        var fcmDeviceId = FirebaseInstanceId.Instance.Token;

        // Settings (is Shared Preferences) - I save the FCMToken Id in shared preferences 
       // if FCMTokenId is not the same as old Token then update on the server

        if (Settings.FcmTokenId != fcmDeviceId)
        {
            var oldFcmId = Settings.FcmTokenId;

            var validationContainer = new ValidationContainer();

            // HERE UPDATE THE TOKEN ON THE SERVER
            TBApp.Current._usersProvider.UpdateFcmTokenOnServer(oldFcmId, fcmDeviceId, validationContainer);

            Settings.FcmTokenId = fcmDeviceId;

        }

        base.OnTokenRefresh();
    }
}

i'm manualy trying to initialize like this

 var options = new FirebaseOptions.Builder()
             .SetApplicationId("YOURAPPID")
             .SetApiKey("YOURAPIKEY")
             //.SetDatabaseUrl(Keys.Firebase.Database_Url) //i'M not using it
             .SetGcmSenderId("YOURSENDERID")
    //.SetStorageBucket(Keys.Firebase.StorageBucket)//i'M not using it
    .Build();
        try
        {   //try to initilize firebase app to get token
            FirebaseApp.InitializeApp(Forms.Context, options);//initializeto get token
        }
        catch
        {   //if app already initialized it will throw exception, so get the previous active token and send to your server-database etc
            var instanceId = FirebaseInstanceId.Instance;
            var token = instanceId.Token;
            Service.MyFirebaseMessagingService.RegisterForAndroid(token); //this method sends the token to my server app, you have to write your own
        }

So when user opens the app, I'm trying to reinitialize the Firebase app. If it is already initalized it will throw an exception :) I'm taking the token there so it gives me the active registered token. If app is not initialized everything will work on smoothly and so your OnTokenRefresh method will be fired as expected. Hope this helps you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM