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

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

Android应用程序应该存储此ID供以后使用(例如,检查onCreate()是否已经注册)。 请注意,Google可能会定期刷新注册ID,因此您应该devise您的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内部完成?

这是一个有趣的问题。

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

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

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

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

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

编辑(06.06.2013):

谷歌改变了他们的演示应用程序使用新的界面。 他们通过设置应用程序本地保存的值的到期date来刷新注册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更改的唯一已知原因是如果应用程序在升级时收到消息,则会自动取消注册的旧bug。 在这个bug修复之前,应用程序在升级之后仍然需要调用register(),到目前为止,注册ID可能会改变。 显式调用unregister()通常也会更改注册标识。

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

这解释了官方GCM Demo应用程序的当前实现。 在使用GoogleCloudMessaging类进行注册时,绝不应使用com.google.android.c2dm.intent.REGISTRATION

读取新的InstanceID API,我发现更多信息关于令牌可能改变的时间:

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

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

更多细节:

Instance ID服务定期(例如,每6个月)启动一次callback,请求您的应用刷新其令牌。 在下列情况下,它也可能启动callback:

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

资料来源:

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

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

在通过networking上的大量误导性答案(包括SO)进行彻底清理之后,唯一find完整答案的地方就是Eran的答案, 这里是 :

虽然自动注册刷新可能或可能永远不会发生,但Google描述了一个通过parsing成功响应来处理canocical_id的简单algorithm:

 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. 

从上述链接。