Android GCM на одном устройстве имеет несколько регистраций

У меня есть система, которая имеет сторону PHP Server и стороннее приложение для Android. Android отправляет параметры через webservice, а PHP обрабатывает сторону GCM. PHP отправляет push-уведомление и перед отправкой, он получает все registrationid из БД. Проблема в том, что одно и то же устройство может иметь два или более регистрационных номера. Из-за этого push-уведомление отправляется на одни и те же устройства в течение двух или более раз. Есть ли решение для решения этой проблемы?

GCM может изменить регистрацию, и вам нужно обновить ее на стороне сервера.

  • Когда вы отправляете сообщение, результат может содержать новый файл registrationId, который будет использоваться для устройства, поэтому вам нужно обновить старый файл registrationId для нового.

Посмотрите эту ссылку в разделе Интерпретация раздела ответа на успех

Если установлено message_id, проверьте для register_id: Если установлено значение register_id, замените исходный идентификатор новым значением (канонический идентификатор) в базе данных вашего сервера. Обратите внимание, что исходный идентификатор не является частью результата, поэтому вам нужно его получить из списка register_id, переданного в запросе (с использованием того же индекса).

Иногда Google меняет идентификатор регистрации, и у вас будет несколько идентификаторов. Сервер, отправляющий уведомление (ваш сервер), должен обновить базу данных новым идентификатором.

Для получения дополнительной информации проверьте этот документ:

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

они говорят:

Это канонические идентификаторы

На стороне сервера, пока приложение ведет себя хорошо, все должно работать нормально. Однако, если ошибка в приложении вызывает несколько регистраций для одного и того же устройства, может быть сложно согласовать состояние, и вы можете получить дубликаты сообщений.

GCM предоставляет средство, называемое «идентификаторы канонической регистрации», чтобы легко восстановить из этих ситуаций. Идентификатор канонической регистрации определяется как идентификатор последней регистрации, запрошенной вашим приложением. Это идентификатор, который сервер должен использовать при отправке сообщений на устройство.

Если позже вы попытаетесь отправить сообщение с использованием другого регистрационного идентификатора, GCM обработает запрос, как обычно, но он будет содержать идентификатор канонической регистрации в поле registration_id ответа. Обязательно замените идентификатор регистрации, хранящийся на вашем сервере, этим каноническим идентификатором, так как в конечном итоге идентификатор, который вы используете, перестанет работать.

Вам просто нужно сохранить идентификатор uniq для каждого устройства в вашей базе данных. Затем вы можете отправить только одно push-уведомление для всех устройств.

Я никогда не видел несколько идентификаторов регистрации для одного и того же устройства. Странная вещь.

Вы правильно управляли обновлением идентификатора регистрации?

Вот мое решение этой проблемы. Когда вы отправляете уведомление 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-идентификаторы, теперь все, что вам нужно сделать, это заменить их gcmids новым, полученным вами в вышеупомянутом массиве, в следующий раз, когда вы отправляете gcm уведомление.

Мое приложение php построено на основе codeigniter, поэтому вот что я сделал. У меня есть модель под названием login, и там у меня есть функция, которая возвращает gcmids из базы данных вместе с идентификатором пользователя пользователя в качестве индекса массива

 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 может отправлять сообщение 1000-битам за раз и sendNotification функцию sendNotification которая возвращает ответ, полученный gcm, который является массивом i, вставленным выше, который имеет все Канонические иды.

То я проверял, не является ли состояние не пустым и является массивом, для получения статуса, потому что он может иметь несколько массивов ответов, потому что я отправляю уведомление в кусках из 1000 идентификаторов, см. Реализацию sendNotification ниже

То я проверил, имеет ли он индекс canonical_ids и больше 0, что означает, что есть идентификаторы, которые необходимо заменить, тогда я проверил, имеет ли он result индекса, а не пуст и является массивом.

Foreach result и получить ключи и значения тех индексов, которые имеют в нем индекс registration_id , что означает, что эти идентификаторы необходимо заменить в нашем db

Получить userid пользователя, gcmid которого нужно заменить, gcmid поиск массива result с идентификатором oldgcm пользователя ( $chunks[$key][$k] , первым параметром является ключ куска, который имеет gcmid Вам нужно, а второй – index этого gcmid )

Получить новый gcmid из массива results

updateGCMId функцию модели updateGCMId , передайте userid и newgcmid .

Здесь реализована sendNotification и 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 :

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

Я думаю, ваша проблема в том, как вы получаете идентификатор регистрации с устройства. Вы должны быть уверены, что он отправляет один раз regID, и когда сервер GCM запрашивает обновление, вы должны повторно отправить regID на ваш веб-сервер и сохранить его, перезаписав старый redID. См. Эту часть ссылки на Android.

Вы получите канонические идентификаторы в своем push-ответе. Вам нужно обновить свой старый идентификатор новыми каноническими идентификаторами.

Есть еще одно решение. Больше похоже на патч. Вы можете передать параметр «dry_run» в запросе GCM JSON. Когда мы установим его в true, он отправит ложное сообщение в идентификатор устройства и сформирует ответ.

Устройство не получит сообщение, но вы получите ответ, поэтому вы можете проверить, какие идентификаторы устройств имеют регистрационные_идентификаторы в своем результате и удалить их из вашей базы данных.

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

Надеюсь, поможет.

Наконец, появилось рабочее решение для повторяющегося регистрационного идентификатора . Его все об обновлении существующего идентификатора регистрации с помощью канонических идентификаторов.

Пример кода Google, который поставляется с бэкэнд, который генерируется из шаблона в Android Studio, делает это за вас. Проверьте класс MessageEndpoint, и вы заметите, что они проверяют канонический идентификатор, и если он существует, то предполагается, что регистр изменился и поэтому идеально нуждается в обновлении для этого конкретного устройства.

 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); } } } } , за 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); } } } }