为啥我会通过 Google Cloud To Device Messaging Service 为我的设备获取多个活动令牌?

Posted

技术标签:

【中文标题】为啥我会通过 Google Cloud To Device Messaging Service 为我的设备获取多个活动令牌?【英文标题】:Why do I get multiple active Tokens for my Device with Googles Cloud To Device Messaging Service?为什么我会通过 Google Cloud To Device Messaging Service 为我的设备获取多个活动令牌? 【发布时间】:2011-08-08 15:33:57 【问题描述】:

我刚刚在我的 android 应用中添加了 C2DM 功能。

如果在我的应用程序中启动 C2DM,则会发生以下情况。

    我的App发送注册Intent 我的应用接收到答案广播

    从 Intent 中检索设备令牌并发送到我的服务器

    从那一刻起,一切正常。客户端接收推送通知等。 如果发生以下情况,则会出现问题:

    用户在未禁用推送的情况下卸载应用程序。 (完全删除不只是更新)

    用户重新安装应用程序

如果在第 5 步之后发送了推送通知,我的应用仍会收到此通知。 从以前的安装中检索到的令牌似乎仍然处于活动状态,并重新连接到我的应用程序的新实例。

这会导致以下问题:

重新安装我的应用程序但无意接收推送通知的用户无法将自己从服务中删除,因为应用程序的新实例无法从我的服务器上取消注册旧令牌。

这是 C2DM 系统中的错误还是我的设置有问题?

更新

我听从了 Berdons 的建议,做了以下事情:

出于测试目的,仅在我的应用每次启动时启动注销 Intent。 在我发送注销意图后,我的服务器没有推送通知发送到我的应用程序。这似乎可以解决问题,但是如果我现在进入 C2DM 设置屏幕并为我的应用打开推送通知,所有旧令牌都会再次激活,并且我会收到我在当前安装我的应用时没有注册的信息。

下次更新

看来我不是唯一一个遇到这个问题的人:

Android C2DM : Duplicate message to the same device and App

我希望 Google 能够管理这些令牌,使来自同一设备的旧令牌在发行新令牌后被禁用。我还希望在我发送注销 Intent 后,该应用程序和该设备的所有令牌都被标记为无效或从 Google 服务器中永久删除。如果这是 Google 针对我看不到的特殊用例做出的某种设计决定,请赐教。

【问题讨论】:

我正在尝试自己实施 C2DM。我的方法是确保用户在我的应用服务器上注册。如果他们卸载它将“在我的应用服务器上禁用他们的帐户,如果他们重新安装,他们必须在我的应用服务器上重新注册(重新启用)他们的帐户。这样,即使令牌有效,您也可以控制何时以及是否向他们推送通知。 @John 您如何注意到用户卸载了您的应用程序? 【参考方案1】:

我们现在找到了适用于大多数情况的解决方案。

服务器将 C2DM 注册 ID 作为数据字段添加到每个 C2D 消息中。

设备现在仅在消息中的令牌与存储在首选项文件中的令牌匹配时才显示通知。这样,我们保证不会显示先前安装获得的设备令牌的消息。 如果令牌不匹配,我们会发现不应再处于活动状态的旧令牌。我们现在将我们自己的 Web 服务器上的令牌标记为非活动状态,以防止服务器发送更多不必要的 C2D 消息

此解决方案使我们能够仅显示相关数据,而无需存储唯一的用户 ID。

【讨论】:

这样我们保证不会显示以前安装获得的设备令牌的消息。 但是当我们卸载应用程序时,保存在 Pref 中的令牌消失了。那么保存这个 Token 的最佳方法是什么? 令牌保存在后端。如果用户重新安装应用程序并激活推送,他会获得一个新令牌,但旧令牌也会收到推送。我只知道新令牌,通过推送旧令牌,我能够过滤所有发送到“错误”令牌的消息。我现在可以从消息中提取错误的令牌并将其从我们的后端中删除。【参考方案2】:

在我的 C2DM 实现中,每个用户的设备令牌都根据他们的 UDID 和应用程序的包名称(以及其他内容)保存在数据库中。 UDID 和包名称构成主键,这意味着该表可以列出来自同一设备 (UDID) 的多个应用程序。当用户运行特定应用程序时,设备令牌会被记录,如果他们卸载并重新运行该应用程序,新设备令牌将被记录在旧设备令牌之上。我们还有一些列来记录该特定应用/设备组合的推送是否处于活动状态,以及用户启用/禁用了哪些类型的推送消息。

当需要为特定应用发送推送时,不会多次注册 UDID(因为这两个字段构成主键),因此只会使用最新的设备令牌。此外,我们的查询只返回启用了推送消息的行。

此解决方案应该可以解决您的问题,因为它会阻止您将推送发送到两个设备令牌。我希望这会有所帮助!

【讨论】:

我认为您的解决方案是做到这一点的唯一方法,但我希望能够逃脱而不必生成除设备令牌以外的其他东西来识别我的用户。如何获得 UDID 的有效值? 最终 TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);字符串设备ID = tm.getDeviceId();您将需要 READ_PHONE_STATE 权限。此外,在研究该主题时,我刚刚了解到 getDeviceId() 仅适用于手机,因此不适用于平板电脑。见***.com/questions/2322234/… 在回复 Robert Peacock 对 Janusz 的回复...如果可以的话,我建议使用 Android_ID 而不是 getDeviceId。请参阅下面的 *** 链接,了解以最佳方式返回 ID 的出色例程。 ***.com/questions/5088474/…【参考方案3】:

更像是一个意想不到的“功能”。您可以考虑在应用程序的“首次运行”(首次运行)时发出取消注册请求,以防止这种情况发生。

更新 您可以通过使用 collapse_key(或您自己创建的任何东西)作为标识符来区分不同的 C2DM 消息。在注册之间更新它,并在注册、注销和消息之后将其传递给设备。

【讨论】:

我试过了,但似乎只有我不注册新的令牌才无效。 你做对了吗?code.google.com/android/c2dm/#unregistering 无论哪种方式,在注销后,如果用户选择不接收它们,只需忽略任何到达的 C2DM - 这可以处理实际上存在注销错误的情况- 顺便说一句,我还没有亲眼目睹。

以上是关于为啥我会通过 Google Cloud To Device Messaging Service 为我的设备获取多个活动令牌?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在通过 Firebase 身份验证传递 id 令牌时,此 CORS 身份验证的 Google Cloud 函数会返回 403?

抛开价格不谈,为啥要选择 Google Cloud Bigtable 而不是 Google Cloud Datastore?

当 Spring Cloud Stream 反应式使用者遇到异常时,为啥我会收到 onComplete 信号?

如何从 Google Cloud text-to-speech API 获取 SSML <mark> 时间戳

为啥 Google 建议将 CloudKMS 应用层加密与 Cloud Storage 结合使用?

为啥@google-cloud/profiler 在 GKE 实例中运行时会抛出“permission_denied”错误?