为多设备、同一用户处理GCM注册ID。

5

这是许多应用程序都面临的常见情况,但我很难理解如何实现:

假设我的应用程序是一个带有当前登录用户的社交网络。我想向该用户发送GCM消息,针对他使用我的应用程序登录的所有设备。

这意味着我的服务器为每个用户保存了他所有设备的注册ID列表 - 每个设备一个注册ID。

问题在于:如何跟踪唯一标识他的每个设备?似乎没有可靠的方法来获取特定设备标识符

如果不为每个唯一设备存储注册ID,我不知道如何管理它。

当用户卸载/注销并在此后获取新的注册ID以替换其中一个已知ID时,情况会变得混乱。

如果我只想向特定设备发送消息而不是所有设备,则情况将变得更加棘手...

请帮助我理解我错过了什么,以及处理同一用户的多个设备注册ID的正确方法是什么。


我不明白你在问什么。是的,服务器保存了所有的注册ID,有什么问题吗? - Xaver Kapeller
但是如果用户反复安装卸载我的应用程序,那么我会为每个安装创建不同的UUID。 - Tal Kanel
1
@TalKanel 不用担心,大部分都由Google处理。如果有变化,你只需要删除多余的并插入新的即可。 - Xaver Kapeller
@XaverKapeller:这正是我不明白如何做到的:我的服务器或客户端如何理解哪些ID已经不再相关。 - Tal Kanel
1
@TalKanel 我正在撰写一篇答案,演示如何处理这个问题。 - Xaver Kapeller
显示剩余3条评论
3个回答

6
谷歌在处理GCM时为您完成了大部分工作。并且他们提供了一种简单的方法来始终保持注册ID更新。每个发送消息中都有一个额外的字段称为canonicalRegistrationId。如果该字段中有一个ID,则表示注册ID已更改,并且需要进行更新。该字段存在于每个消息中,每次发送消息时,都必须检查该字段。如果出现新的canonicalRegistrationId,则应尽快更新registrationId。旧的ID可能会继续工作一段时间,但无法确定何时会失效。
例如,在Google App Engine后端处理更改注册ID的代码如下:
// We want to send a message to some devices
// Get registered devices and then loop through them
List<RegistrationRecord> records = ofy().load().type(RegistrationRecord.class).limit(10).list();
for(RegistrationRecord record : records) {

    // Send the message to one device
    Result result = sender.send(msg, record.getRegId(), 5);

    // If the messageId is not null, then the message has been sent successfully
    if (result.getMessageId() != null) {
        log.info("Message sent to " + record.getRegId());

        // Check for canonical message id
        // If it is not null, then the registrationId has changed
        String canonicalRegId = result.getCanonicalRegistrationId();
        if (canonicalRegId != null) {

            // The registrationId has changed! We need to update it in the database
            log.info("Registration Id changed for " + record.getRegId() + " updating to " + canonicalRegId);
            record.setRegId(canonicalRegId);
            ofy().save().entity(record).now();
        }
    } else {
        ... // Irrelevant error handling
    }
}

所以你需要做的很简单:每次发送消息时,检查是否有canonicalRegistrationId,如果有,则用规范的注册ID更新旧的注册ID。


1
我理解注册ID是唯一的。我的意思是以下情况可能发生:1)电话生成新的注册ID=[x],并将此ID发送到服务器。2)服务器创建RegistrationRecord并分配注册ID=[x]。 - Tal Kanel
1
  1. 手机因某些原因获得了新的注册 ID = [y],并将其发送到服务器。4) 服务器存储新的 RegistrationRecord,其中它的 registration id = [y]。5) 服务器迭代所有注册记录,并在调用 sender.send() 方法时获取响应的 canonicalRegId = [y],对于 regId [x]。6) 根据您的代码 - 服务器更改此记录的注册 ID 为 [y]。现在您将拥有两条持有 regId = [y] 的记录。我错在哪里了?
- Tal Kanel
1
@TalKanel是正确的,如果注册ID已更改,则可能客户端已将其通知服务器,服务器将其添加到列表中。因此,您代码中的解决方案应该很简单:仅在新的注册ID不存在时删除旧的注册ID并添加新的注册ID。 - bluish
1
@Andrew 我现在告诉你,如果你走上那条路并开始发送带有GCM消息的实际数据,那么你将遇到一系列问题。 - Xaver Kapeller
1
@Andrew 如果你想这样做,那就去做吧,我只是说一下。这就是GCM应该工作的方式,肯定不需要修复。 - Xaver Kapeller
显示剩余16条评论

2
Google支持设备组消息。请参考此链接:https://developers.google.com/cloud-messaging/notifications#managing_device_groups
要处理多个用户,您可以为该用户创建一个群组,并将注册ID添加到该群组中。要创建一个群组,请向以下地址发送如下请求:https://android.googleapis.com/gcm/notification
Content-Type:application/json Authorization:key=API_KEY project_id:SENDER_ID

{ "operation": "create", "notification_key_name": "appUser-Chris", "registration_ids": ["4", "8", "15", "16", "23", "42"] }

此API返回通知密钥,您可以将其存储在数据库中,以后可用于向该组发送通知。随后,每当用户登录到其他设备时,您可以获取该设备的reg_id并将其添加到该组中。要将具有注册ID 51的设备添加到appUser-Chris,请发送此请求:

{ "operation": "add", "notification_key_name": "appUser-Chris", "notification_key": "aUniqueKey", "registration_ids": ["51"] }

当用户注销设备时,您可以从组中删除reg_id

向组发送通知与向注册ID发送通知相同。

我在此处观察到的一个问题是,如果您在创建组时未能将notification_key存储到数据库中,则无法再次获取它。而且不能再次为该notification_key_name创建新组。解决此问题的方法是明智地选择您的notification_key_name。因此,当您未能存储notification_key并尝试再次创建具有相同noitification_key_name的组时,将会出现错误。在这种情况下,您只需更改该用户的notification_key_name并创建一个新组即可。希望这可以帮助您。


1
我认为您需要获取设备IMEI号并将其与注册ID一起保存到服务器上,当用户使用其他设备重新注册时,获取检查IMEI号并用新的替换旧的。现在主要问题是,在旧设备上检查发送IMEI号与消息,并且如果IMEI号匹配,则没有问题,否则会阻止应用程序并通知用户...就这样。

一些 Wi-Fi 平板电脑是否可能没有 IMEI 号码? - hanamj
@hanamj:是的,这是可能的。 - Jayesh Khasatiya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接