2013-05-29 16 views
5

PHPサーバー側とAndroidクライアント側アプリケーションがあるシステムがあります。 AndroidはWebサービス経由でパラメータを送信し、PHPはGCM側を処理します。 PHPはプッシュ通知を送信し、送信前にDBからすべての登録IDを取得します。問題は、同じデバイスが2つ以上のregistrationidを持つ可能性があるということです。このため、プッシュ通知は2回以上同じデバイスに送信されました。この問題を処理するソリューションはありますか?Android GCM 1つのデバイスに複数の登録IDがあります

答えて

2

データベース内の各デバイスにユニークIDを格納するだけで済みます。次に、すべてのデバイスに対してプッシュ通知を1回だけ送信できます。

同じデバイスで複数の登録IDが表示されることはありません。奇妙なこと。

登録IDの更新を正しく管理しましたか?

+0

ありがとうございますが、主な問題は更新しようとしていません。 scenerioでは、ユーザーは多くのアンドロイドデバイスを持つことがあります。このため、3つのデバイスには7つのregistrationIdがあります。私は3プッシュメッセージを送信したいが、GCMは7を送信する。 – iravul

0

あなたの問題は、デバイスから登録IDを取得する方法だと思います。 regIDを1回送信する必要があります。また、GCMサーバーがリフレッシュを要求するときには、regIDをWebサーバーに再送信して保存し、古いredIDを上書きする必要があります。 アンドロイド参照のthisを参照してください。

+0

ありがとうございますが、主な問題は更新しようとしていません。 scenerioでは、ユーザーは多くのアンドロイドデバイスを持つことがあります。このため、3つのデバイスには7つのregistrationIdがあります。私は3プッシュメッセージを送信したいが、GCMは7を送信する。 – iravul

+0

あなたは私の応答を理解していない...すべてのデバイスで、Google GCMサーバーは定期的にIDを更新したい...アンドロイドのAPIドキュメントをチェックする...エラーは最初です:各デバイスは1つのregIDを時間内に持つ必要があります。 – trullallero

4

GCMはregistrationIdを変更することができ、サーバー側で更新する必要があります。

  • メッセージを送信すると、デバイスに使用される新しいregistrationIdが結果に含まれる可能性があります。そのため、新しいregistrationIdを更新する必要があります。

ルックMESSAGE_IDが設定されている場合、REGISTRATION_IDをチェックlink at Interpreting a success response section

この:REGISTRATION_IDが設定されている場合は、サーバーのデータベースに新しい値(正規のID)と、元のIDを交換してください。元のIDは結果の一部ではないので、リクエストで渡されたregistration_idsのリストから取得する必要があります(同じインデックスを使用)。

+0

ありがとうございますが、主な問題は更新しようとしていません。 scenerioでは、ユーザーは多くのアンドロイドデバイスを持つことがあります。このため、3つのデバイスには7つのregistrationIdがあります。 3プッシュメッセージを送信しますが、GCMは7を送信します。 – iravul

+0

GCMサービスでは、1つのregistrationIdによってデバイス/アプリケーションが識別されるため、GCMを使用して3つのデバイスがサーバーに接続されている場合は3つのregIdが必要です。 regIdが 'GCMRegistrar.getRegistrationId'にまだ保存されている場合は、' GCMRegistrar.register'の前にチェックしてください。必要以上に生成しています。 – AlexBcn

+0

GCMチェック端末(bbb)を送信し、user2またはuser1がこの通知を受け取らない場合、(terminal1)gcm(bbb)を変更した場合、(terminal1)gcm(aaa)login user1およびuser2。誤解! – delive

3

時々、Googleが登録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が機能しなくなります。

+0

私は同じデバイス上の2つの異なる登録IDで問題に直面していますが、0のcanonical_idsが戻ります。 – neobie

0

プッシュレスポンスで標準的なIDを取得します。古いIDを新しい正準IDで更新する必要があります。

解決策がもう1つあります。パッチに似ています。 GCMリクエストのJSONでパラメータ 'dry_run'を渡すことができます。これをtrueに設定すると、デバイスIDに偽のメッセージが送信され、応答が生成されます。

デバイスにはメッセージは表示されませんが、応答が得られるため、結果にregistration_idが含まれているデバイスIDを確認してデータベースから削除できます。

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

希望します。

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 
        ) 

      ) 

    ) 

のような応答がuは今、すべてを行う必要があり、4人のユーザーが新しいGCM IDを生成している持っていることを意味している4であるインデックスcanonical_idsあり得ます次にgcm通知を送信したときに、gcmidを上記の配列で受け取った新しいものに置き換えてください。

私のPHPアプリケーションはcodeigniterフレームワーク上に構築されていますので、ここで私が行ったことです。 私は、ログインと呼ばれるモデルを持っており、そこに私は、アレイ

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... 
) 
のように見えるように、ユーザーのユーザーIDとともにデータベースからgcmidsを返す関数を持っています

指数は、ここで、この機能は、コントローラの関数内で呼び出された値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は一度に1000個のメッセージにメッセージを送ることができ、sendNotificationという関数を呼び出し、gcmが受け取った応答を返します。すべての標準的なIDを持ちます。

ステータスが空でなく、配列であるならば、私はその後、

以下sendNotification実装を参照してください、私は1000年、IDSのチャンクに通知を送信していますので、それが複数の応答配列を有することができるので、状況をforeachのチェックそれがインデックスcanonical_idsを持っていて、0より大きいかどうかチェックされます。つまり、置き換えが必要なIDがある場合、インデックスがresultで、空ではなく配列であるかどうかがチェックされます。

foreachのresult及びこれらのIDを意味し、それにregistration_id率を有するこれらのインデックスのキーと値を取得するには、私たちのデシベル

gcmidニーズによって交換するユーザーのuseridを得るに交換する必要がありますユーザーのoldgcm IDで、result配列を検索

$chunks[$key][$k]、最初のパラメータは、必要なgcmidと第二を持っているチャンクのキーはそのgcmidindexである)新しいgcmidからの取得resultsアレイ

モデル関数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

は最後にDuplicate Registration Idのための作業溶液を得ました。既存の登録IDを標準IDで更新することに関するすべてです。

0

Androidスタジオでテンプレートから生成されたバックエンドに付属する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); 
      } 
     } 
    } 
} 
関連する問題