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


78

在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类在内部完成的?

Answers:


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仍然起作用(导致Google响应中发送了规范的注册ID),因此没有造成任何伤害。

编辑(06.06.2013):

Google更改了其演示应用程序以使用新界面。他们通过在应用程序本地保留的值上设置过期日期来刷新注册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再次更改了他们的演示应用程序(两天前)。这次,他们删除了认为注册ID在7天后过期的逻辑。现在,他们仅在安装新版本的应用程序时刷新注册ID。

编辑(04.24.2014):

为了完整起见,以下是参与GCM开发的Google开发人员Costin Manolache的话(从此处获取):

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

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

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

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


我认为您的回答是正确的,regid的任何更改都可以由项目的应用服务器中的php或jsp响应来处理。不过,现在是Google更新与SDK捆绑在一起的演示客户端/服务器代码的时候了!
NickT

Google的文档似乎已过时。实现时,GCMBaseIntentService类具有被调用的onUnregistered。没有说明在刷新注册ID时是否调用该方法。GCM的“入门”部分甚至没有提到GCMBaseIntentService,但包含在示例代码中。Google确实需要清理他们的文档。
AndroidDev 2013年

@AndroidDev是的,他们的文档目前有点混乱。但是,他们确实指出有多种实现GCM的方法。入门显示了新方法,但是(在第3步中)提到了另一种方法,并链接到它。GCMBaseIntentService在“使用GCM帮助器库”页面中仍然提到。
Eran

19
+1修改(04.24.2014)旧的SO问题和答案被误导并浪费了我的时间...希望每个人都对其旧答案进行编辑,不再有效
Harshad Ranganathan 2014年

2
我们将需要对GCM 3.0进行编辑。developers.google.com/cloud-messaging/android/start
霍朗2015年

6

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

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

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

更多细节:

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

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

资料来源:

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

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


2

在遍历整个网络(包括SO)的大量误导性答案之后,我发现唯一完整答案的地方就是Eran的答案,在这里

尽管自动注册刷新可能会或可能永远不会发生,但google描述了一种通过解析成功的响应来处理canocical_id的类似算法:

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.

从上述链接。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.