首页 文章

Android GCM一个设备有多个registrationid

提问于
浏览
5

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

8 回答

  • 0

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

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

    看这个link at Interpreting a success response section

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

  • 2

    有时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将停止工作 .

  • 0

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

    我从未看到同一设备的多个注册ID . 奇怪的事情 .

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

  • 0

    这是我解决这个问题的方法 . 当您发送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,现在你需要做的就是用你在上面的数组中收到的新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的用户标识

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

    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接收到的响应,这是我上面粘贴的所有数据规范的ids .

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

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

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

    通过搜索 result 数组获取 gcmid 需要替换的用户的 userid ,用户的oldgcm id( $chunks[$key][$k] ,第一个参数是你需要 gcmid 的块的键,第二个是 index 的那个 gcmid

    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);
    }
    
  • 4

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

  • 2

    您将在推送响应中获得规范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
                        );
    

    希望能帮助到你 .

  • 3

    终于得到了Duplicate Registration Id的工作解决方案 . 它的全部内容是使用规范ID更新现有的注册ID .

  • 0

    从Android Studio中的模板生成的后端附带的Google示例代码可以帮到您 . 查看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);
                }
            }
        }
    }
    

相关问题