阅读背景:

处理Android上的Google Cloud Messaging中的注册ID更改

来源:互联网 

In the docs on Google Cloud Messaging, it states:

在Google云消息传递的文档中,它指出:

The Android application should store this ID for later use (for instance, to check on onCreate() if it is already registered). 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.

Android应用程序应存储此ID以供以后使用(例如,如果已注册,则检查onCreate())。请注意,Google可能会定期刷新注册ID,因此您应该设计Android应用程序,并了解可能会多次调用com.google.android.c2dm.intent.REGISTRATION意图。您的Android应用程序需要能够做出相应的响应。

I register my device using the following code:

我使用以下代码注册我的设备:

GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
String regID = gcm.register(senderID);

The GoogleCloudMessaging class encapsulates the registration process. So how am I suppose to handle com.google.android.c2dm.intent.REGISTRATION since handling that is done internally by the GoogleCloudMessaging class?

GoogleCloudMessaging类封装了注册过程。那么我想如何处理com.google.android.c2dm.intent.REGISTRATION,因为处理是由GoogleCloudMessaging类在内部完成的?

3 个解决方案

#1


136  

That's an interesting question.

这是一个有趣的问题。

Google encourage you to switch to the new registration process :

Google建议您切换到新的注册流程:

An Android application running on a mobile device registers to receive messages by calling the GoogleCloudMessaging method register(senderID...). This method registers the application for GCM and returns the registration ID. This streamlined approach replaces the previous GCM registration process.

运行在移动设备上的Android应用程序通过调用GoogleCloudMessaging方法寄存器(senderID ...)来注册接收消息。此方法为GCM注册应用程序并返回注册ID。这种简化的方法取代了之前的GCM注册流程。

The note that says Google may periodically refresh the registration ID only appears on the page that still shows the old registration process, so it's possible that this note is no longer relevant.

Google可能会定期刷新注册ID的说明仅显示在仍显示旧注册过程的页面上,因此该注释可能不再相关。

If you want to be safe, you can still use the old registration process. Or you can use the new process, but have in addition the code that handles the com.google.android.c2dm.intent.REGISTRATION intent, in order to make sure you are covered if Google do decide to refresh the registration ID.

如果您想要安全,您仍然可以使用旧的注册流程。或者您可以使用新流程,但另外还有处理com.google.android.c2dm.intent.REGISTRATION意图的代码,以确保在Google决定刷新注册ID时您将受到保护。

That said, I never experienced such a refresh, and even when I did experience a change in the registration ID (usually as a result of sending a notification after un-installing the app and then re-installing it), the old registration ID still worked (resulting in a canonical registration ID sent in the response from Google), so no harm was done.

也就是说,我从未经历过这样的刷新,即使我确实经历了注册ID的更改(通常是在卸载应用程序然后重新安装之后发送通知的结果),旧的注册ID仍然工作(导致在Google的回复中发送的规范注册ID),因此没有造成任何伤害。

EDIT (06.06.2013) :

编辑(06.06.2013):

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.

谷歌改变了他们的演示应用程序以使用新界面。他们通过在应用程序本地持久保存的值上设置过期日期来刷新注册ID。当应用程序启动时,它们会加载本地存储的注册ID。如果它“过期”(在演示中意味着它是在7天前从GCM收到的),他们再次调用gcm.register(senderID)。

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.

这不适用于Google针对尚未启动很长时间的应用更新注册ID的假设方案。在这种情况下,应用程序将不会知道更改,第三方服务器也不会。

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;
}

EDIT (08.14.2013) :

编辑(08.14.2013):

Google changed their Demo App again (two days ago). This time they removed the logic that considers the Registration ID to be expired after 7 days. Now they only refresh the Registration ID when a new version of the app it installed.

谷歌再次改变了他们的演示应用程序(两天前)。这次他们删除了7天后认为注册ID过期的逻辑。现在,他们只在安装了新版本的应用程序时刷新注册ID。

EDIT (04.24.2014) :

编辑(04.24.2014):

For the sake of completeness, here are the words of Costin Manolache (taken from here), a Google developer involved in the development of GCM, on the matter :

为了完整起见,以下是Costin Manolache(取自此处)的话,这是一位参与GCM开发的Google开发人员,关于此事:

The 'periodical' refresh never happened, and the registration refresh is not included in the new GCM library.

“周期性”刷新从未发生过,新的GCM库中不包含注册刷新。

The only known cause for registration ID change is the old bug of apps getting unregistered automatically if they receive a message while getting upgraded. Until this bug is fixed apps still need to call register() after upgrade, and so far the registration ID may change in this case. Calling unregister() explicitly usually changes the registration ID too.

注册ID更改的唯一已知原因是,如果应用程序在升级时收到消息,则会自动取消注册。在修复此错误之前,应用程序仍需要在升级后调用register(),到目前为止,注册ID可能会更改。显式调用unregister()通常也会更改注册ID。

The suggestion/workaround is to generate your own random identifier, saved as a shared preference for example. On each app upgrade you can upload the identifier and the potentially new registration ID. This may also help tracking and debugging the upgrade and registration changes on server side.

建议/解决方法是生成您自己的随机标识符,例如保存为共享首选项。在每次应用升级时,您都可以上传标识符和可能的新注册ID。这也可以帮助跟踪和调试服务器端的升级和注册更改。

This explains the current implementation of the official GCM Demo application. com.google.android.c2dm.intent.REGISTRATION should never be handled when using the GoogleCloudMessaging class to register.

这解释了官方GCM演示应用程序的当前实现。使用GoogleCloudMessaging类进行注册时,永远不应该处理com.google.android.c2dm.intent.REGISTRATION。

#2


6  

Reading the new InstanceID API, I found more info on when the token might change:

阅读新的InstanceID API,我发现有关令牌可能更改的更多信息:

Your app can request tokens from the Instance ID service as needed using the getToken() method, and like InstanceID, your app can also store tokens on your own server. All tokens issued to your app belong to the app's InstanceID.

您的应用可以根据需要使用getToken()方法从Instance ID服务请求令牌,并且像InstanceID一样,您的应用也可以在您自己的服务器上存储令牌。发给您应用的所有令牌都属于该应用的InstanceID。

Tokens are unique and secure, but your app or the Instance ID service may need to refresh tokens in the event of a security issue or when a user uninstalls and reinstalls your app during device restoration. Your app must implement a listener to respond to token refresh requests from the Instance ID service.

令牌是唯一且安全的,但是您的应用或Instance ID服务可能需要在出现安全问题或用户在设备恢复期间卸载并重新安装应用时刷新令牌。您的应用必须实现侦听器以响应来自Instance ID服务的令牌刷新请求。

More details:

更多细节:

The Instance ID service initiates callbacks periodically (for example, every 6 months), requesting that your app refreshes its tokens. It may also initiate callbacks when:

Instance ID服务定期启动回调(例如,每6个月),请求您的应用刷新其令牌。它可能还会在以下情况下启动回调:

  • There are security issues; for example, SSL or platform issues.
  • 存在安全问题;例如,SSL或平台问题。
  • Device information is no longer valid; for example, backup and restore.
  • 设备信息不再有效;例如,备份和还原。
  • The Instance ID service is otherwise affected.
  • 实例ID服务受到其他影响。

Sources:

资料来源:

https://developers.google.com/instance-id/

https://developers.google.com/instance-id/

https://developers.google.com/instance-id/guides/android-implementation

https://developers.google.com/instance-id/guides/android-implementation

#3


2  

After scrubbing through tonnes of misleading answers across the net, including SO, the only place I found a complete answer was as remarked by Eran's answer and here:

在通过网络(包括SO)清除大量误导性答案之后,我找到一个完整答案的唯一地方就是Eran的回答和这里所说的:

While automatic registration refresh might or might never have happened, google describes a simiple algorithm to handle the canocical_ids by parsing successful response:

虽然自动注册刷新可能会或可能永远不会发生,但谷歌通过解析成功响应来描述一个simiple算法来处理canocical_ids:

If the value of failure and canonical_ids is 0, it's not necessary to parse the remainder of the response. Otherwise, we recommend that you iterate through the results field and do the following for each object in that list:

If message_id is set, check for registration_id:
If registration_id is set, replace the original ID with the new value (canonical ID) in your server database. Note that the original ID is not part of the result, so you need to obtain it from the list of code>registration_ids passed in the request (using the same index).
Otherwise, get the value of error:
If it is Unavailable, you could retry to send it in another request.
If it is NotRegistered, you should remove the registration ID from your server database because the application was uninstalled from the device or it does not have a broadcast receiver configured to receive com.google.android.c2dm.intent.RECEIVE intents.
Otherwise, there is something wrong in the registration ID passed in the request; it is probably a non-recoverable error that will also require removing the registration from the server database. See Interpreting an error response for all possible error values.

From aforementioned link.

从上述链接。


分享到: