Reputation: 408
I have system that has PHP Server side and Android Client side application. Android sends parameters via webservice and PHP handle the GCM side. PHP sends push notification and before sent, it gets all registrationid from DB. The problem is, same device may have two or more registrationid. Because of this, push notification sent to same devices for two or more times. Is there any solution to handle this problem?
Upvotes: 5
Views: 7550
Reputation: 89
Google's sample code that comes with the backend that gets generated from the template in Android Studio does this for you. Check out the MessageEndpoint class and you'll notice that They are checking for a canonical id and if it exists then the assumption is that the regid has changed and therefore ideally needs to be updated for that particular device.
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);
}
}
}
}
Upvotes: 0
Reputation: 31
Finally got working Solution for Duplicate Registration Id . Its all about updating existing registration id with canonical ids.
Upvotes: 0
Reputation: 2247
here is my solution to this problem. when you send a gcm notification you get a response like this
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
)
)
)
in the array there is an index canonical_ids
which is 4 that means u have 4 users have generated new gcm ids, now all you need to do is replace their gcmids with the new one you received in the above array, next time when you send gcm notification.
my php app is built on codeigniter framework so here is what i did. i have a model called login and in there i have a function that returns gcmids from database along with the userid of the user as an index of the array
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;
}
}
the array looks like this
Array(
[29] => APA91bEYda3DVb...
[1] => APA91bF0yfdZjX4...
[12] => APA91bG-9fsBGT-...
[11] => APA91bGNRh_VWF...
[3] => APA91bGXo_gnfBZ...
[2] => APA91bH3WdWwqFC...
[26] => APA91bHn8Ufwe4...
)
index is the userid with value gcmid
this function is called inside controller function, here is the implementation
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);
}
as you in the comments i have broken the array into chunks of 1000 ids because gcm can send message to 1000ids at a time, and called a function sendNotification
that return the response received by gcm, which is the array i pasted above that has all the canonical ids.
then i checked if the status is not empty and is an array, foreach the status because it may have multiple response arrays because i am sending notification in chunks of 1000 ids , see the sendNotification
implementation below
then i checked if it has a index canonical_ids
and is greater than 0, which means there are ids that needs to be replaced, then i checked if it has the index result
, not empty and is an array.
foreach result
and get the keys and values of those index that has registration_id
index in it, which means these ids needs to be replaced in our db
get the userid
of the user whose gcmid
needs to be replaced by searching the result
array, with the oldgcm id of the user($chunks[$key][$k]
, the first parameter is the key of the chunk that has the gcmid
you need and second is the index
of that gcmid
)
get the new gcmid
from the results
array
call the model function updateGCMId
, pass the userid
and newgcmid
.
here is the implementation of sendNotification
and updateGCMId
functions.
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
function:
public function updateGCMId($userid,$gcmid){
$query = 'UPDATE users_app SET
gcmid = "'.$gcmid.'"
WHERE id = "'.$userid.'"';
$this->db->query($query);
}
Upvotes: 2
Reputation: 107
You will get canonical Ids in your push response. You need to update your Old Id with new canonical Ids.
There is one more solution. More like a patch. You can pass parameter 'dry_run' in GCM request JSON. When we set it to true, it will send a fake message to device Id and generate a response.
Device will not get a message but you will get your response, hence you can check which device Ids have registration_ids in their result and remove them from your database.
$fields = array(
'registration_ids' => $deviceId,
'data' => array( "message" =>'fake_message'),
'dry_run'=>true
);
Hope it helps.
Upvotes: 0
Reputation: 5591
Sometimes Google changes the registration ID and you'll have multiple IDs associated. The server that sends the notification (your server) has to update the database with the new ID.
For more info check this document:
http://developer.android.com/google/gcm/adv.html
they say:
It's an Canonical IDs
On the server side, as long as the application is behaving well, everything should work normally. However, if a bug in the application triggers multiple registrations for the same device, it can be hard to reconcile state and you might end up with duplicate messages.
GCM provides a facility called "canonical registration IDs" to easily recover from these situations. A canonical registration ID is defined to be the ID of the last registration requested by your application. This is the ID that the server should use when sending messages to the device.
If later on you try to send a message using a different registration ID, GCM will process the request as usual, but it will include the canonical registration ID in the registration_id field of the response. Make sure to replace the registration ID stored in your server with this canonical ID, as eventually the ID you're using will stop working.
Upvotes: 3
Reputation: 1076
You just have to store a uniq id for each device in your database. Then, you can send just one push notification for every devices.
I've never see multiple registration id for the same device. Strange thing.
Did you manage the registration id update correctly ?
Upvotes: 2
Reputation: 2460
GCM can change the registrationId and you have to update it on your server-side.
Look this link at Interpreting a success response section
If message_id is set, check for registration_id: If registration_id is set, replace the original ID with the new value (canonical ID) in your server database. Note that the original ID is not part of the result, so you need to obtain it from the list of registration_ids passed in the request (using the same index).
Upvotes: 4
Reputation:
I think your problem is how you get the registration id from the device. You must be sure that it sends one time the regID and when the GCM server request a refresh, you must resend the regID to your webserver and store it, overwriting the old redID. See this part of android reference.
Upvotes: 0