Google message "Not registered" and unsubscribe?

I am developing an Android application using Xamarin Forms, in which the main goal is to push event notifications. I had some seemingly random problem with receiving failures Not Registeredwhen sending a notification after a successful device call GcmPubSub.getInstance().subscribe(). This happened a week or 2 ago, and I decided that the problem was solved, always using the main application context to generate a token and call getInstance().

Yesterday around noon EST, the problem recurred, and then just as suddenly started working around 4:00 - 4:30. The afternoon was full of code comments to simplify things and other random things like removing and re-adding NuGet packages. Now I returned to the code that was in my place before it stopped working yesterday, and everything was happy, like a clam.

When this problem occurs, only when the call subscribe()is made via Wi-Fi. If I debug an application on my phone on a cellular network, I never get a crash Not Registered.

I am currently invoking unsubscribe()when a user exits the application in the application and I was able to unsubscribe and re-subscribe successfully (this morning).

Disable disconnect on transition from best practice for push notifications when notifications are user-specific? I thought it might be possible that the GCM servers were somehow confused.

Any suggestions as to why I might get setbacks Not Registeredwould also be awesome.

Registration services (subscription / unsubscribe):

namespace MyApp.Droid.Services
{
    /// <summary>
    /// The background process that handles retrieving GCM token
    /// </summary>
    [Service(Exported = true)]
    public class GcmRegistrationService : IntentService
    {
        private static readonly object Locker = new object();

        public GcmRegistrationService() : base("GcmRegistrationService") { }

        public static Intent GetIntent(Context context, string topic)
        {
            var valuesForActivity = new Bundle();
            valuesForActivity.PutString("topic", topic);

            var intent = new Intent(context, typeof(GcmRegistrationService));

            intent.PutExtras(valuesForActivity);

            return intent;
        }

        protected override async void OnHandleIntent(Intent intent)
        {
            try
            {
                // Get the count value passed to us from MainActivity:
                var topic = intent.Extras.GetString("topic", "");

                if (string.IsNullOrWhiteSpace(topic))
                    throw new Java.Lang.Exception("Missing topic value");

                string token;

                Log.Info("RegistrationIntentService", "Calling InstanceID.GetToken");
                lock (Locker)
                {
                    var instanceId = InstanceID.GetInstance(Forms.Context);
                    var projectNumber = Resources.GetString(Resource.String.ProjectNumber);
                    token = instanceId.GetToken(projectNumber, GoogleCloudMessaging.InstanceIdScope, null);

                    Log.Info("RegistrationIntentService", "GCM Registration Token: " + token);

                    Subscribe(token, topic);
                }

                var applicationState = ApplicationStateService.GetApplicationState ();

                // Save the token to the server if the user is logged in
                if(applicationState.IsAuthenticated)
                    await SendRegistrationToAppServer(token);
            }
            catch (SecurityException e)
            {
                Log.Debug("RegistrationIntentService", "Failed to get a registration token because of a security exception");
                Log.Debug ("RegistrationIntentService", "Exception message: " + e.Message);
                //ToastHelper.ShowStatus("Google Cloud Messaging Security Error");
                throw;
            }
            catch (Java.Lang.Exception e)
            {
                Log.Debug("RegistrationIntentService", "Failed to get a registration token");
                Log.Debug ("RegistrationIntentService", "Exception message: " + e.Message);
                //ToastHelper.ShowStatus("Google Cloud Messaging Error");
                throw;
            }

        }

        private async System.Threading.Tasks.Task SendRegistrationToAppServer(string token)
        {
            // Save the Auth Token on the server so messages can be pushed to the device
            await DeviceService.UpdateCloudMessageToken (token);

        }

        void Subscribe(string token, string topic)
        {

            var pubSub = GcmPubSub.GetInstance(Forms.Context);

            pubSub.Subscribe(token, "/topics/" + topic, null);
            Log.Debug("RegistrationIntentService", "Successfully subscribed to /topics/" +topic);
            ApplicationStateService.SaveCloudMessageToken(token, topic);
        }

    }


    /// <summary>
    /// The background process that handles unsubscribing GCM token
    /// </summary>
    [Service(Exported = false)]
    public class GcmUnsubscribeService : IntentService
    {

        private static readonly object Locker = new object();

        public GcmUnsubscribeService() : base("GcmUnsubscribeService") { }

        public static Intent GetIntent(Context context, ApplicationState applicationState, bool resubscribe=false)
        {
            var valuesForActivity = new Bundle();

            valuesForActivity.PutString ("token", applicationState.CloudMessageToken);
            valuesForActivity.PutString ("topic", applicationState.Topic);
            valuesForActivity.PutBoolean ("resubscribe", resubscribe);

            var intent = new Intent(context, typeof(GcmUnsubscribeService));

            intent.PutExtras(valuesForActivity);

            return intent;
        }

        protected override void OnHandleIntent(Intent intent)
        {

            // Get the count value passed to us from MainActivity:
            var token = intent.Extras.GetString("token", "");
            var topic = intent.Extras.GetString("topic", "");
            var resubscribe = intent.Extras.GetBoolean ("resubscribe");

            var pubSub = GcmPubSub.GetInstance(Forms.Context);
            try
            {
                pubSub.Unsubscribe (token, "/topics/" + topic);
            }
            catch(IOException e) 
            {
                var x = e.Message;
            }

            if (resubscribe) {
                var subscribeIntent = GcmRegistrationService.GetIntent(Forms.Context, topic);
                Forms.Context.StartService(subscribeIntent);
            }
        }
    }
}

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:installLocation="auto" 
    package="com.me.notification_app" 
    android:versionCode="1" 
    android:versionName="1.0">

    <uses-sdk android:minSdkVersion="19" />

    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <permission 
        android:name="com.me.notification_app.permission.C2D_MESSAGE" 
        android:protectionLevel="signature" />

    <uses-permission 
        android:name="com.me.notification_app.permission.C2D_MESSAGE" />

    <application 
        android:label="Notification App" 
        android:icon="@drawable/icon">

        <receiver 
            android:name="com.google.android.gms.gcm.GcmReceiver" 
            android:permission="com.google.android.c2dm.permission.SEND"
            android:exported="true">

            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="com.me.notification_app" />
            </intent-filter>

        </receiver>

    </application>
</manifest>

Primary activity:

[Activity(Label = "MyApp", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
    public static string NotificationTopic = "MyEvent";

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

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

        if (IsPlayServicesAvailable())
        {
            var intent = GcmRegistrationService.GetIntent(this, NotificationTopic);
            StartService(intent);
        }
    }


    public bool IsPlayServicesAvailable()
    {
        var resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
        if (resultCode != ConnectionResult.Success)
        {
            if (GoogleApiAvailability.Instance.IsUserResolvableError(resultCode))
                ToastHelper.ShowStatus("Google Play Services error: " + GoogleApiAvailability.Instance.GetErrorString(resultCode));
            else
            {
                ToastHelper.ShowStatus("Sorry, notifications are not supported");
            }
            return false;
        }
        else
        {                
            return true;
        }
    }

}

Sending notifications on the server side. Device.CloudMessageTokenpopulated by a call DeviceService.UpdateCloudMessageToken (token)to the registration service above:

public async Task SendNotificationAsync(Device device, string message, Dictionary<string, string> extraData = null)
{
    if (string.IsNullOrWhiteSpace(device.CloudMessageToken))
        throw new Exception("Device is missing a CloudMessageToken");

    var apiKey = _appSettingsHelper.GetValue("GoogleApiKey");
    var gcmBaseUrl = _appSettingsHelper.GetValue("GoogleCloudMessageBaseUrl");
    var gcmSendPath = _appSettingsHelper.GetValue("GoogleCloudMessageSendPath");

    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri(gcmBaseUrl);
        client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "key=" + apiKey);
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));


        var messageInfo = new MessageInfo
        {
            to = device.CloudMessageToken,
            data = new Dictionary<string, string>
            {
                {"message", message}
            }
        };

        if (extraData != null)
        {
            foreach (var data in extraData)
            {
                messageInfo.data.Add(data.Key, data.Value);
            }
        }

        var messageInfoJson = JsonConvert.SerializeObject(messageInfo);

        var response =
            await
                client.PostAsync(gcmSendPath,
                    new StringContent(messageInfoJson, Encoding.UTF8, "application/json"));

        response.EnsureSuccessStatusCode();

        var content = await response.Content.ReadAsStringAsync();

        var contentValues = JsonConvert.DeserializeObject<Dictionary<string, object>>(content);

        if ((long)contentValues["failure"] == 1)
        {
            var results = (JArray)contentValues["results"];
            throw new Exception(results[0]["error"].ToString());
        }

    }

}
+4
source share
2 answers

, , , . , , , InstanceId , Not Registered. , InstanceId , .

unsubscribe() . @gerardnimo

, InstanceId ( , , InstanceId), GcmRegistrationService

/// <summary>
/// Gcm reregistration service to delete and recreate the token.
/// </summary>
[Service(Exported = false)]
public class GcmReregistrationService : IntentService
{

    private static readonly object Locker = new object();

    public GcmReregistrationService() : base("GcmReregistrationService") { }

    public static Intent GetIntent(Context context, string token, string topic)
    {
        var valuesForActivity = new Bundle();

        valuesForActivity.PutString ("token", token);
        valuesForActivity.PutString ("topic", topic);

        var intent = new Intent(context, typeof(GcmReregistrationService));

        intent.PutExtras(valuesForActivity);

        return intent;
    }

    protected override void OnHandleIntent(Intent intent)
    {

        // Get the count value passed to us from MainActivity:
        var token = intent.Extras.GetString("token", "");
        var topic = intent.Extras.GetString("topic", "");

        var instanceId = InstanceID.GetInstance(Forms.Context);
        instanceId.DeleteToken (token, GoogleCloudMessaging.InstanceIdScope);
        instanceId.DeleteInstanceID ();

        var subscribeIntent = GcmRegistrationService.GetIntent(Forms.Context, topic);
        Forms.Context.StartService(subscribeIntent);

    }
}
+4

Xamarin, , Xamarin Studio - APK, , GetToken(), .

Xamarin Studio DeleteInstanceID() .

, , APK ( , Xamarin Studio), .

private bool IsForcedTokenRefreshNeeded()
{
    DateTime actualWriteDT = GetAPKLastWriteDT();
    DateTime? storedLastWriteDT = RetrieveAPKLastWriteDT();
    bool forceTokenRefresh = false;
    if (storedLastWriteDT.HasValue)
    {
        if (actualWriteDT != storedLastWriteDT)
        {
            forceTokenRefresh = true;
            StoreAPKLastWriteDT(actualWriteDT);
        }
    }
    else
    {
        StoreAPKLastWriteDT(actualWriteDT); 
    }
    return forceTokenRefresh;
}

private void StoreAPKLastWriteDT(DateTime lastWriteDT)
{
    var prefs = Application.Context.GetSharedPreferences("MyApp", FileCreationMode.Private);
    var prefEditor = prefs.Edit();
    prefEditor.PutLong("APKLastModified", lastWriteDT.Ticks);
    prefEditor.Commit();
}

private DateTime? RetrieveAPKLastWriteDT()
{
    //retreive 
    var prefs = Application.Context.GetSharedPreferences("MyApp", FileCreationMode.Private);              
    long value = prefs.GetLong("APKLastModified", 0);
    if (value == 0)
    {
        return null;
    }
    return new DateTime(value);
}

private DateTime GetAPKLastWriteDT()
{
    string packageName = Android.App.Application.Context.PackageName;
    Android.Content.PM.ApplicationInfo appInfo = this.PackageManager.GetApplicationInfo(packageName, 0);
    string appFile = appInfo.SourceDir;
    return new FileInfo(appFile).LastWriteTimeUtc;
}

GcmRegistrationService:

protected override void OnHandleIntent (Intent intent)
{
    Log.Info("GcmRegistrationService", "Calling InstanceID.GetToken");
    string token;
    bool forceTokenRefresh = IsForcedTokenRefreshNeeded();
    try
    {
        lock (m_lock)
        {
            InstanceID instanceID = InstanceID.GetInstance (Android.App.Application.Context);
            if (forceTokenRefresh)
            {
                Log.Info("GcmRegistrationService", "Forced token refresh");
                instanceID.DeleteInstanceID();
            }
            token = instanceID.GetToken(SenderID, GoogleCloudMessaging.InstanceIdScope, null);
            Log.Info("GcmRegistrationService", "GCM Registration Token: " + token);
        }
    }
    catch (Exception ex)
    {
        Log.Debug("GcmRegistrationService", "Failed to get a registration token: " + ex.Message);
        return;
    }

    try
    {
        SendRegistrationToAppServer(token);
    }
    catch(WebException)
    {
        if (forceTokenRefresh)
        {
            // this will force a refresh next time
            StoreAPKLastWriteDT(DateTime.MinValue);
        }
    }

    try
    {
        Subscribe(token);
    }
    catch (Exception ex)
    {
        Log.Debug("GcmRegistrationService", "Failed to subscribe: " + ex.Message);
        return;
    }
}
+2

Source: https://habr.com/ru/post/1622101/


All Articles