Android GCM一个设备有多个registrationid

时间:2013-05-29 07:55:01

标签: php android push-notification google-cloud-messaging

我的系统有PHP Server端和Android Client端应用程序。 Android通过webservice发送参数,PHP处理GCM端。 PHP发送推送通知,在发送之前,它从DB获取所有注册。问题是,同一设备可能有两个或更多注册。因此,将推送通知发送到相同设备两次或更多次。有没有办法解决这个问题?

8 个答案:

答案 0 :(得分:4)

GCM可以更改registrationId,您必须在服务器端更新它。

  • 当您发送消息时,结果可能包含将用于设备的新registrationId,因此您必须更新旧的registrationId。

看这个link at Interpreting a success response section

  

如果设置了message_id,请检查registration_id:如果设置了registration_id,请将原始ID替换为服务器数据库中的新值(规范ID)。请注意,原始ID不是结果的一部分,因此您需要从请求中传递的registration_ids列表中获取它(使用相同的索引)。

答案 1 :(得分:3)

有时Google会更改注册ID,您将拥有多个ID。发送通知的服务器(您的服务器)必须使用新ID更新数据库。

有关详细信息,请查看此文档:

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

他们说:

这是Canonical ID

在服务器端,只要应用程序运行良好,一切都应该正常工作。但是,如果应用程序中的错误触发了同一设备的多次注册,则可能很难协调状态,并且您最终可能会收到重复的消息。

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

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

答案 2 :(得分:2)

您只需为数据库中的每个设备存储uniq id。然后,您只需为每个设备发送一个推送通知。

我从未看到同一设备的多个注册ID。奇怪的是。

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

答案 3 :(得分: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个用户生成了新的gcm id,现在你需要做的就是用你在上面的数组中收到的新gcmids替换它们的gcmids ,下次发送gcm通知时。

我的php应用程序是基于codeigniter框架构建的,所以这就是我所做的。 我有一个名为登录的模型,在那里我有一个函数,从数据库返回gcmids以及用户的用户ID作为数组的索引

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是值为gcmid

的用户ID

这个函数在控制器函数内部调用,这里是实现

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个块,因为gcm可以一次向1000ids发送消息,并调用函数sendNotification来返回gcm接收的响应,这是数组我粘贴在上面有所有规范的ID。

然后我检查状态是否为空并且是一个数组,因为它可能有多个响应数组,因为我在1000个ID的块中发送通知,请参阅下面的sendNotification实现

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

foreach result并获取其中包含registration_id索引的索引的键和值,这意味着需要在我们的数据库中替换这些ID

使用用户的oldgcm id(userid,第一个参数)搜索gcmid数组,获取需要替换result的用户的$chunks[$key][$k]是你需要gcmid的块的关键,其次是index的{​​{1}}

gcmid数组

获取新的gcmid

调用模型函数results,传递updateGCMIduserid

这是newgcmidsendNotification函数的实现。

updateGCMId

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

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

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

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

希望它有所帮助。

答案 6 :(得分:0)

终于找到了Duplicate Registration Id的解决方案。它只是用规范ID更新现有的注册ID。

答案 7 :(得分:0)

Google在Android Studio中从模板生成的后端附带的示例代码可以帮到您。查看MessageEndpoint类,您会注意到它们正在检查规范ID,如果存在,则假设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);
            }
        }
    }
}