Android GCM一个设备有多个registration ID

5

我有一个系统,其中包括PHP服务器端和Android客户端应用程序。Android通过Web服务发送参数,PHP处理GCM方面。PHP发送推送通知,在发送之前,从数据库获取所有注册ID。问题是,同一设备可能具有两个或更多的注册ID。因此,推送通知会多次发送到相同的设备。是否有解决此问题的方法?

8个回答

4
GCM 可以更改 registrationId,您需要在服务器端更新它。
当您发送一条消息时,结果可能包含将用于设备的新 registrationId,因此您需要更新旧的 registrationId 为新的 registrationId。
请参阅这个链接中的“解释成功响应”部分:http://developer.android.com/google/gcm/gcm.html#push-process 如果设置了 message_id,请检查 registration_id:如果设置了 registration_id,请在服务器数据库中使用新值(规范 ID)替换原始 ID。请注意,原始 ID 不是结果的一部分,因此您需要从请求中传递的 registration_ids 列表中获取它(使用相同的索引)。

谢谢你的帮助,但主要问题不在于更新。在这种情况下,用户可能有多个安卓设备。因此,3个设备可能有7个registrationId。我想发送3条推送消息,但GCM会发送7条。 - iravul
通过GCM服务,一个registrationId可以识别一个设备/应用程序,因此如果用户使用GCM连接到您的服务器有3个设备,则应该有3个regId。在执行GCMRegistrar.register之前,请检查您的regId是否仍存储在GCMRegistrar.getRegistrationId上,以避免生成多余的注册ID。 - AlexBcn
不好的想法,因为:(终端1)gcm(aaa)登录用户1和用户2,如果你改变(终端1)gcm(bbb),如果你发送一个GCM检查终端(bbb),用户2或用户1将无法收到此通知。这是一个误解! - user3402040

3
有时候谷歌会更改注册ID,导致您有多个关联的ID。发送通知(您的服务器)的服务器必须使用新的ID更新数据库。
有关更多信息,请查阅此文档:

http://developer.android.com/google/gcm/adv.html

他们说:

这是一个规范化的ID

在服务器端,只要应用程序表现良好,一切都应该正常工作。然而,如果应用程序中的错误触发了同一设备的多个注册,很难协调状态,你可能会得到重复的消息。

GCM提供了一个名为“规范化注册ID”的功能,以便轻松地从这些情况中恢复。规范化注册ID被定义为应用程序请求的最后一个注册ID。这是服务器在向设备发送消息时应该使用的ID。

如果之后您尝试使用不同的注册ID发送消息,GCM将像往常一样处理请求,但它将在响应的registration_id字段中包含规范化注册ID。请确保用此规范化ID替换存储在服务器上的注册ID,因为您正在使用的ID最终将停止工作。


我在同一设备上遇到了两个不同的注册ID问题,但是返回0个规范ID。 - neobie

2

您只需要在数据库中为每个设备存储一个唯一的ID。然后,您可以为每个设备发送一条推送通知。

我从未见过同一设备有多个注册ID的情况。这很奇怪。

您是否正确管理了注册ID的更新?


谢谢你的帮助,但主要问题不在于更新。在这种情况下,用户可能有多个安卓设备。因此,3个设备可能有7个registrationId。我想发送3条推送消息,但GCM会发送7条。 - iravul

2
这是我对这个问题的解决方案。 当您发送GCM通知时,您会收到以下响应。
Array
    (
        [multicast_id] => 12345678910
        [success] => 8
        [failure] => 3
        [canonical_ids] => 4
        [results] => Array
            (
                [0] => Array
                    (
                        [error] => NotRegistered
                    )

                [1] => Array
                    (
                        [message_id] => 0:1412242641156904%3dc89e2df9fd7ecd
                    )

                [2] => Array
                    (
                        [registration_id] => APA91bH3WdWwqFCVKbvZXsf-gj28iWU5oYbSCRZYFp987CHasxwT_HOiE7dp8212XID0FMGVG2n4NLohFsEGYJ-LEA07xsgsKfT00xModQcx5QgTBmJtxlWgeaWFpz29z-iCPYbvOHEhqfwHmN-HIps7DiWntcs-Qg
                        [message_id] => 0:1412242641155639%3dc89e2df9fd7ecd
                    )

                [3] => Array
                    (
                        [registration_id] => APA91bH3WdWwqFCVKbvZXsf-gj28iWU5oYbSCRZYFp987CHasxwT_HOiE7dp8212XID0FMGVG2n4NLohFsEGYJ-LEA07xsgsKfT00xModQcx5QgTBmJtxlWgeaWFpz29z-iCPYbvOHEhqfwHmN-HIps7DiWntcs-Qg
                        [message_id] => 0:1412242641155809%3dc89e2df9fd7ecd
                    )


                [4] => Array
                    (
                        [message_id] => 0:1412242641157971%3dc89e2df9fd7ecd
                    )


                [5] => Array
                    (
                        [registration_id] => APA91bGXo_gnfBZsvPoqJTYy1BWz0FQIkwlD1EmBtcxgWWfceYvd0ehWqVCtfd8n56VGYrvXCS2v48kTiA69BD7Sci0BA9a9bKTIg_MUEnDd79ssCK-miPG88DDGL4oKtB14cPbh-_xbgVRZllMOzwNZf_w5uJGR8g
                        [message_id] => 0:1412242641157969%3dc89e2df9fd7ecd
                    )


                [6] => Array
                    (
                        [registration_id] => APA91bGXo_gnfBZsvPoqJTYy1BWz0FQIkwlD1EmBtcxgWWfceYvd0ehWqVCtfd8n56VGYrvXCS2v48kTiA69BD7Sci0BA9a9bKTIg_MUEnDd79ssCK-miPG88DDGL4oKtB14cPbh-_xbgVRZllMOzwNZf_w5uJGR8g
                        [message_id] => 0:1412242641157967%3dc89e2df9fd7ecd
                    )

            )

    )

在数组中有一个索引canonical_ids,其值为4,这意味着您有4个用户生成了新的gcm id,现在您需要做的就是用上述数组中收到的新id替换它们的gcm id,在下一次发送gcm通知时使用。

我的php应用程序建立在CodeIgniter框架上,所以我做了以下操作。 我有一个名为“login”的模型,在其中有一个函数,该函数从数据库返回gcmids和用户的userid作为数组的索引。

function getAllRegIds(){
    $query = "SELECT gcmid,id FROM users_app WHERE gcmid IS NOT NULL AND gcmid != '' GROUP BY gcmid"; //group by incase a user loggedin with multiple userids in your a
    $query = $this->db->query($query);

    if($query->num_rows() > 0){
        $result = $query->result_array();
        $regids = array();

        foreach($result as $row){
            $regids[$row['id']] = $row['gcmid'];
        }
        return $regids; 
    }else{
        return false;
    }
}

数组看起来像这样。
Array(
  [29] => APA91bEYda3DVb...
  [1] => APA91bF0yfdZjX4...
  [12] => APA91bG-9fsBGT-...
  [11] => APA91bGNRh_VWF...
  [3] => APA91bGXo_gnfBZ...
  [2] => APA91bH3WdWwqFC...
  [26] => APA91bHn8Ufwe4...
)

index是userid,其值为gcmid。

这个函数被调用在控制器函数内部,以下是代码实现:

public function sendtoall(){
    $this->load->model('app/login');

    $result = $this->login->getAllRegIds(); //here i got all gcmids with userids as index

    $message = $this->input->post('message');
    $chunks = array_chunk($result,1000); //broken the array into chunks of 1000 ids because gcm can only send message to 1000 ids at a time

    $status = $this->sendNotification($chunks,$message);


    if(!empty($status) && is_array($status)){
        foreach($status as $key=>$row){

            if(array_key_exists('canonical_ids',$row) && $row['canonical_ids'] > 0){
                $canonical_ids = $row['canonical_ids'];

                if(array_key_exists('results',$row) && !empty($row['results']) && is_array($row['results'])){
                    foreach($row['results'] as $k=>$v){
                        if(array_key_exists('registration_id',$v)){

                            $userid = array_search($chunks[$key][$k], $result);
                            $newgcmid  = $v['registration_id'];

                            $this->login->updateGCMId($userid,$newgcmid);
                        }
                    }
                }
            }
        }
    }

    echo json_encode($status);
}

根据评论中的建议,我将数组分成了包含1000个ID的块,因为GCM一次只能发送1000个ID的消息,并调用一个名为sendNotification的函数,它返回由GCM接收的响应,该响应是我上面粘贴的数组,其中包含所有规范化的ID。

然后我检查状态是否不为空且为数组,对于状态进行循环处理,因为它可能具有多个响应数组,因为我将通知按1000个ID的块发送,请参见下面的sendNotification实现。

然后我检查它是否具有索引canonical_ids并大于0,这意味着需要替换的ID,然后我检查它是否具有索引result,不为空并且是一个数组。

对于每个result,获取其中具有registration_id索引的键和值,这意味着需要在我们的数据库中替换这些ID。

通过搜索result数组以获取需要替换其gcmid的用户的userid(使用用户的旧gcmid$chunks [$ key] [$ k],第一个参数是带有所需gcmid的块的键,第二个是该gcmidindex)。)

results数组中获取新的gcmid

调用模型函数updateGCMId,传递useridnewgcmid

下面是sendNotificationupdateGCMId函数的实现。

private function sendNotification($regids,$message){
    $status = array();
    $result = $regids;
    if($result){
        foreach($result as $thousandids){
            $registatoin_ids=$thousandids;

            $msg=array("message"=>$message);



            $url='https://android.googleapis.com/gcm/send';
            $fields=array
             (
              'registration_ids'=>$registatoin_ids,
              'data'=>$msg
             );
            $headers=array
             (
              'Authorization: key=AIza..............',
              'Content-Type: application/json'
             );
            $ch=curl_init();
            curl_setopt($ch,CURLOPT_URL,$url);
            curl_setopt($ch,CURLOPT_POST,true);
            curl_setopt($ch,CURLOPT_HTTPHEADER,$headers);
            curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
            curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
            curl_setopt($ch,CURLOPT_POSTFIELDS,json_encode($fields));
            $result=curl_exec($ch);
            curl_close($ch);


            $result = json_decode($result,1);

            $status[] = $result;

        }
    }

    return $status;
}

update函数:

public function updateGCMId($userid,$gcmid){
      $query = 'UPDATE users_app SET 
                  gcmid = "'.$gcmid.'" 
                WHERE id = "'.$userid.'"';
      $this->db->query($query);
}

0

谷歌提供的样例代码,它会为从Android Studio模板生成的后端自动完成此操作。查看MessageEndpoint类,你会注意到他们正在检查规范ID是否存在,如果存在,则假设regid已更改,因此最好更新该特定设备的regid。

public void sendMessage(@Named("message") String message) throws IOException {
    if (message == null || message.trim().length() == 0) {
        log.warning("Not sending message because it is empty");
        return;
    }
    // crop longer messages
    if (message.length() > 1000) {
        message = message.substring(0, 1000) + "[...]";
    }
    Sender sender = new Sender(API_KEY);
    Message msg = new Message.Builder().addData("message", message).build();
    List<RegistrationRecord> records = ofy().load().type(RegistrationRecord.class).limit(10).list();
    for (RegistrationRecord record : records) {
        Result result = sender.send(msg, record.getRegId(), 5);
        if (result.getMessageId() != null) {
            log.info("Message sent to " + record.getRegId());
            String canonicalRegId = result.getCanonicalRegistrationId();
            if (canonicalRegId != null) {
                // if the regId changed, we have to update the datastore
                log.info("Registration Id changed for " + record.getRegId() + " updating to " + canonicalRegId);
                record.setRegId(canonicalRegId);
                ofy().save().entity(record).now();
            }
        } else {
            String error = result.getErrorCodeName();
            if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
                log.warning("Registration Id " + record.getRegId() + " no longer registered with GCM, removing from datastore");
                // if the device is no longer registered with Gcm, remove it from the datastore
                ofy().delete().entity(record).now();
            } else {
                log.warning("Error when sending message : " + error);
            }
        }
    }
}

0

在推送响应中,您将获得规范化的ID。您需要使用新的规范化ID更新旧ID。

还有一种解决方案。更像是一个补丁。您可以在GCM请求JSON中传递参数“dry_run”。当我们将其设置为true时,它将向设备ID发送虚假消息并生成响应。

设备将不会收到消息,但您将获得响应,因此您可以检查哪些设备ID在其结果中具有registration_ids,并将其从数据库中删除。

        $fields = array(
                    'registration_ids'  => $deviceId,
                    'data' => array( "message" =>'fake_message'),
                    'dry_run'=>true
                    );

希望能有所帮助。

0

我认为你的问题是如何从设备获取注册ID。 你必须确保它只发送一次regID,当GCM服务器请求刷新时,你必须重新发送regID到你的web服务器并存储它,覆盖旧的redID。 请参见android参考this部分。


谢谢你的帮助,但主要问题不在于更新。在这种情况下,用户可能有多个安卓设备。因此,3个设备可能有7个registrationId。我想发送3条推送消息,但GCM会发送7条。 - iravul
你不理解我的回答...对于每个设备,Google GCM服务器需要定期刷新ID...请查看Android API文档...错误在于开始时:每个设备一次只能有一个regID。 - user1432059

0

终于找到了重复注册ID的解决方案。这一切都与使用规范ID更新现有注册ID有关。


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