79

在 Google Cloud Messaging 上的文档中,它指出:

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

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

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

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

4

3 回答 3

137

这是一个有趣的问题。

Google 鼓励您切换到新的注册流程:

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

说的注释Google may periodically refresh the registration ID只出现在仍然显示旧注册过程的页面上,因此该注释可能不再相关。

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

也就是说,我从来没有经历过这样的刷新,即使我确实经历了注册ID的变化(通常是由于卸载应用程序然后重新安装后发送通知的结果),旧的注册ID仍然存在工作(导致在谷歌的响应中发送一个规范的注册 ID),所以没有造成任何伤害。

编辑(06.06.2013):

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

这不处理 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;
}

编辑(08.14.2013):

Google 再次更改了他们的Demo App(两天前)。这次他们删除了认为注册 ID 将在 7 天后过期的逻辑。现在他们只在安装新版本的应用程序时刷新注册 ID。

编辑(04.24.2014):

为完整起见,以下是参与 GCM 开发的 Google 开发人员Costin Manolache(摘自此处)在此问题上的话:

“定期”刷新从未发生,并且注册刷新不包含在新的 GCM 库中。

注册 ID 更改的唯一已知原因是应用程序在升级时收到消息会自动取消注册的旧错误。在修复此错误之前,应用程序在升级后仍然需要调用 register(),并且到目前为止注册 ID 可能会在这种情况下发生变化。显式调用 unregister() 通常也会更改注册 ID。

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

这解释了官方 GCM Demo 应用程序的当前实现。 使用类注册com.google.android.c2dm.intent.REGISTRATION时千万不要处理。GoogleCloudMessaging

于 2013-05-30T14:51:24.203 回答
6

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

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

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

更多细节:

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

  • 存在安全问题;例如,SSL 或平台问题。
  • 设备信息不再有效;例如,备份和恢复。
  • 否则会影响实例 ID 服务。

资料来源:

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

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

于 2015-09-11T08:33:58.147 回答
2

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

虽然自动注册刷新可能会或可能永远不会发生,但 google 描述了一种通过解析成功响应来处理 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.

从上述链接。

于 2014-09-07T06:37:32.717 回答