处理 Android Google云消息传递中的注册 ID 更改

在 Google云消息传递的文件中写道:

Android 应用程序应该存储此 ID 以供以后使用(用于 如果已经注册,则检查 onCreate () 谷歌可能会定期刷新注册 ID,所以你 设计 Android 应用程序时,应理解 可以调用 com.google.android.c2dm.intent.Registry 意图 您的 Android 应用程序需要能够响应 因此。

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

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

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

56315 次浏览

这是个有趣的问题。

谷歌鼓励你转向新的注册程序:

在移动设备上运行的 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 从谷歌发送的响应) ,所以没有造成任何损害。

编辑(2013年6月6日) :

谷歌改变了他们的 演示应用程序使用新的界面。它们通过在应用程序本地持久化的值上设置过期日期来刷新注册 ID。当应用程序启动时,它们加载本地存储的注册 ID。如果它是“过期的”(在演示中意味着它是从 GCM 超过7天前收到) ,他们再次调用 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;
}

编辑(2013年8月14日) :

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

编辑(2014年4月24日) :

为了完整起见,以下是参与 GCM 开发的谷歌开发人员 Costin Manolache (摘自 给你)的话:

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

注册 ID 变更的唯一已知原因是应用程序的老 bug 如果他们收到一条消息,就会自动取消注册 正在升级。在这个错误修复之前,应用程序仍然需要调用 Register ()在升级之后,到目前为止注册 ID 可能在 显式调用 unregister ()通常会更改 还有登记证。

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

这解释了当前正式 GCM 演示应用程序的实现。 使用 GoogleCloudMessaging类注册时,不应处理 com.google.android.c2dm.intent.REGISTRATION

在网上搜索了大量误导性的答案(包括 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.

来自上述链接。

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

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

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

更多细节:

Instance ID 服务定期发起回调(例如, (每6个月更新一次) ,要求你的应用程序刷新其令牌 也会在下列情况下启动回调:

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

资料来源:

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

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