5

As the question says that How to find out when does the registration ID has become invalid in GoogleCloudMessaging API? I already read the answers on few questions on similar topic: Do GCM registration id's expire? and Google Coud Mesaging (GCM) and registration_id expiry, how will I know? . The issue with those question is that the answers there are for C2DMor old GCM API which used GCMRegistrar instead of GoogleCloudMessaging API. The previous two methods have been depreciated.

I'll try to break my confusion/question stepwise:

1) Under the heading Enable GCM, in the second point it says:

Google may periodically refresh the registration ID, so you should design your Android application with the understanding that the com.google.android.c2dm.intent.REGISTRATION intent may be called multiple times. Your Android application needs to be able to respond accordingly.

The registration ID lasts until the Android application explicitly unregisters itself, or until Google refreshes the registration ID for your Android application. Whenever the application receives a com.google.android.c2dm.intent.REGISTRATION intent with a registration_id extra, it should save the ID for future use, pass it to the 3rd-party server to complete the registration, and keep track of whether the server completed the registration. If the server fails to complete the registration, it should try again or unregister from GCM.

2) Now, if that's the case then I should handle the intent in a BroadcastReceiver and send the register() request again to get a new registration ID. But the issue is that on the same page under heading ERROR_MAIN_THREAD, it says that: GCM methods are blocking. You should not run them in the main thread or in broadcast receivers.

3) I also understand that there are other two scenarios when the registration ID changes( as mentioned under Advanced Topics under heading Keeping the Registration State in Sync): Application update and Backup&restore. I am already handling them on opening of the app.

4) In GCMRegistrar API, inside GCMBaseIntentService, there used to be a callback onRegistered() method, which got called when the device got registered. Here I used to persist the registration ID and send to 3rd party servers.

But, now How should I handle the updation or renewal of the registration ID, persist it and send it to 3rd party server?

It might be that either I am getting confused by reading all of it or I am missing something. I would be really thankful for your help.

Update

Even on Handling registration ID changes in Google Cloud Messaging on Android thread, there is no mentioning of how to handle the periodic refreshing of ID by Google?

Community
  • 1
  • 1
Shobhit Puri
  • 25,769
  • 11
  • 95
  • 124
  • Possible duplicate of this - http://stackoverflow.com/questions/16838654/handling-registration-id-changes-in-google-cloud-messaging-on-android/16839326#16839326 – Eran Jun 27 '13 at 10:10

2 Answers2

4

I am giving a way as What I implemented in my application

@Override
protected void onRegistered(Context context, String registrationId) {
     Log.i(TAG, "Device registered: regId = " + registrationId);
     //displayMessage(context, getString(R.string.gcm_registered));
     //ServerUtilities.register(context, registrationId);
     //1. Store this id to application Prefs on each request of device registration
     //2. Clear this id from app prefs on each request of device un-registration  
     //3. Now add an if check for new registartion id to server, you can write a method on server side to check if this reg-id matching for this device or not (and you need an unique identification of device to be stored on server)
     //4. That method will clear that if id is matching it meanse this is existing reg-id, and if not matching this is updated reg-id.
     //5. If this is updated reg-id, update on server and update into application prefs.
}

You can do like this also

if reg_id exists_into prefrences then
    if stored_id equals_to new_reg_id then
        do nothing
    else
        say server to reg_id updated
        update prefrences with new id
    end if
else
    update this id to application prefs
    say server that your device is registered
end if

But problem arises when, user clears the application data and you will loose the current reg-id.


Update for new API example Credits goes to Eran and His Answer Handling registration ID changes in Google Cloud Messaging on Android

Google changed their Demo App to use the new interface. They refresh the registration ID by setting an expiration date on the value persisted locally by the app. When the app starts, they load their locally stored registration id. If it is "expired" (which in the demo means it was received from GCM over 7 days ago), they call gcm.register(senderID) again.

This doesn't handle the hypothetical scenario in which a registration ID is refreshed by Google for an app that hasn't been launched for a long time. In that case, the app won't be aware of the change, and neither will the 3rd party server.

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);
    mDisplay = (TextView) findViewById(R.id.display);

    context = getApplicationContext();
    regid = getRegistrationId(context);

    if (regid.length() == 0) {
        registerBackground();
    }
    gcm = GoogleCloudMessaging.getInstance(this);
}

/**
 * Gets the current registration id for application on GCM service.
 * <p>
 * If result is empty, the registration has failed.
 *
 * @return registration id, or empty string if the registration is not
 *         complete.
 */
private String getRegistrationId(Context context) {
    final SharedPreferences prefs = getGCMPreferences(context);
    String registrationId = prefs.getString(PROPERTY_REG_ID, "");
    if (registrationId.length() == 0) {
        Log.v(TAG, "Registration not found.");
        return "";
    }
    // check if app was updated; if so, it must clear registration id to
    // avoid a race condition if GCM sends a message
    int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
    int currentVersion = getAppVersion(context);
    if (registeredVersion != currentVersion || isRegistrationExpired()) {
        Log.v(TAG, "App version changed or registration expired.");
        return "";
    }
    return registrationId;
}

/**
 * Checks if the registration has expired.
 *
 * <p>To avoid the scenario where the device sends the registration to the
 * server but the server loses it, the app developer may choose to re-register
 * after REGISTRATION_EXPIRY_TIME_MS.
 *
 * @return true if the registration has expired.
 */
private boolean isRegistrationExpired() {
    final SharedPreferences prefs = getGCMPreferences(context);
    // checks if the information is not stale
    long expirationTime =
            prefs.getLong(PROPERTY_ON_SERVER_EXPIRATION_TIME, -1);
    return System.currentTimeMillis() > expirationTime;
}
Community
  • 1
  • 1
Pankaj Kumar
  • 81,967
  • 29
  • 167
  • 186
  • Thanks for the answer Pankaj. Did you do this inside service [GCMBaseIntentService](http://developer.android.com/reference/com/google/android/gcm/GCMBaseIntentService.html) ? When I was using [GCMRegistrar](http://developer.android.com/reference/com/google/android/gcm/GCMRegistrar.html), I did use this intent service as you did. – Shobhit Puri Jun 27 '13 at 06:20
  • Oh Yaa.. sorry I meant `GCMIntentService` which extends `GCMBaseIntentService`? That has been depreciated right? ( But still works) – Shobhit Puri Jun 27 '13 at 06:24
  • I haven't been able to find anywhere how to take care of that using `GoogleCloudMessaging` API. Even the sample code doesn't speak of that. Please if you ever come across its working in the latest API, do edit the answer or provide suggestions. I would be really thankful. – Shobhit Puri Jun 27 '13 at 06:34
  • hey are you asking about GCM? I think this is not deprecated yet. And I am confused ehich API you are talking about. Can share the doc link? – Pankaj Kumar Jun 27 '13 at 06:37
  • If you see the [GCMRegistrar](http://developer.android.com/reference/com/google/android/gcm/GCMRegistrar.html) or [GCMBaseIntentService](http://developer.android.com/reference/com/google/android/gcm/GCMBaseIntentService.html), it says that it is depricated and instead we should use [GoogleCloudMessaging](http://developer.android.com/reference/com/google/android/gms/gcm/GoogleCloudMessaging.html). It does not say anything of how to use `GCMIntentService` etc( if possible). – Shobhit Puri Jun 27 '13 at 06:48
  • Ohh yes you are right. So you should implement a Receiver which will handle your message. It will be great if you read codes of gcm-client-lib and make your idea. I have not enouph time to do RnD now. So I can't. But when I will do. I will update this answer as well. OK? – Pankaj Kumar Jun 27 '13 at 06:56
  • Cool. Thanks sir for your guidance. I have implemented the receiver but the docs say that we [should not call the GCM functions inside the receiver](http://developer.android.com/reference/com/google/android/gms/gcm/GoogleCloudMessaging.html#ERROR_MAIN_THREAD) as they are blocking. Nevertheless, I'll try to figure it out. Thanks for your time again :) – Shobhit Puri Jun 27 '13 at 06:58
  • Thats why I am saying that to see code of gcm-client. I am giving you a idea. when gcm sends any type of message the listen into receiver and do some task depends on message type into BaseIntentService and then your IntentService get call back. – Pankaj Kumar Jun 27 '13 at 07:02
  • ohh I see. okay. I'll continue with that idea and will post if I am able to solve. – Shobhit Puri Jun 27 '13 at 07:06
  • @ShobhitPuri Here is the demo code for new API https://code.google.com/p/gcm/source/browse/gcm-client/src/com/google/android/gcm/demo/app/ this will clear your doubts. – Pankaj Kumar Jun 28 '13 at 05:31
  • Thanks for the link. I did check this code before sir. For updating the registration ID, It only checks for the conditions of 1) If key is more than 7 days old then renew it. 2) If the app is updated.. It still does not say about the condition where Google updates the key by sending `com.google.android.c2dm.intent.REGISTRATION` intent. – Shobhit Puri Jun 28 '13 at 05:38
  • Yes you are right and for this you can implement my first or second algo that must work... I am sure about it. I had only confusion about use of new API and that you got now. So just try to implement the logic of algo. And you will be done. All the best.. – Pankaj Kumar Jun 28 '13 at 05:41
1

Just to add to Pankaj's answer:

  • This(the example on getting started documents by Google) doesn't handle the hypothetical scenario in which a registration ID is refreshed by Google for an app that hasn't been launched for a long time. In that case, the app won't be aware of the change, and neither will the 3rd party server.

    Its true that the example on Getting started documentation does not handle that case. So the developer need to handle himself.

  • Also the answer says that They refresh the registration ID by setting an expiration date on the value persisted locally by the app. When the app starts, they load their locally stored registration id. If it is "expired" they call gcm.register(senderID) again.

    The issue is that the seven days local expiry of the registration ID in the sample is to avoid the scenario where the device sends the registration to the 3rd party server but the server loses it. It does not handle the refreshing of the ID from Google servers.

  • The second point under the heading Enable GCM on Architectural Overview page, it says:

    Note that Google may periodically refresh the registration ID, so you should design your Android application with the understanding that the com.google.android.c2dm.intent.REGISTRATION intent may be called multiple times. Your Android application needs to be able to respond accordingly.

    So, for handling that you should have a Broadcast Listener which could handle com.google.android.c2dm.intent.REGISTRATION intent, which Google send to the app when it has to refresh the registration ID.

  • There is another part of the question which states about the problem is that inside the Broadcast Listener I cannot call register the for Push ID again. This is because the documentation says: GCM methods are blocking. You should not run them in the main thread or in broadcast receiver.

    I think that issue is completely different from the statement. When you register a broadcast receiver, it will have an Intent which will contain the new registration ID from Google. I DON'T need to call gcm.register() method again in the Broadcast listener.

Hope this helps someone understand how to handle the renewal of registration ID.

Shobhit Puri
  • 25,769
  • 11
  • 95
  • 124
  • Its a humble request that if you are down voting the answer, kindly comment to explain further. This will help me improve the answer and will make me understand your views on the answer content. Although the previous answer helped but didn't actually answer/solves the confusion, so I answered it again. If you think that there is any criticism in the answer, please do speak up. It will clear more concepts. Thanks again! – Shobhit Puri Aug 01 '13 at 05:15
  • 1
    I'm not sure why this is getting downvoted. It is very clear and helpful. Upvote from me. – KyleT Jun 27 '14 at 00:18
  • whatever you wrote don't answer question and you are confused between old and new api. either old would be used or new would be used – Gaurav Singla Sep 24 '14 at 07:02