web-dev-qa-db-ja.com

Googleクラウドメッセージング-ネットワークの状態が変化するまでメッセージが受信されない場合がある

GCMと統合する小さなプロジェクトで作業しているときに、少し奇妙な問題に遭遇しました。

メッセージを受信したかどうかを確認するためにログの監視を開始すると、ネットワーク状態を変更するまでメッセージが届いていないように見えることがあります(つまり、元々はWiFiでしたが、WiFiをオフにしてモバイルデータに移動すると、メッセージが届きます結構です)。ネットワーク状態を変更した後、メッセージは完全に正常に到着し始めます。ネットワーク状態を以前の状態(この場合はWiFi)に戻すと、メッセージは引き続き受信されます。

プロジェクト自体には、起動時に開始する(起動時にGCMBaseIntentServiceを開始する)機能が含まれていますが、これも完全に正常に機能し、この問題が発生したときにアプリを手動で起動したため、アプリ/サービスが実行されていると確信しています(これにより、また、サービスが実行されているかどうかを確認し、実行されていない場合は実行し、登録されているかどうかを確認します)。

他の誰かがこの問題に遭遇しましたか、または私がこれをどのように解決できるかについての指針はありますか?メッセージが受信されていないときと、メッセージが受信されたとき(ネットワークの状態を変更した後)の間に、ログに何のヘルプも表示されません。 GCMドキュメントを確認しましたが、(デバイス自体の)タイムアウト、またはこれに影響を与える可能性のある構成オプションが原因で受信されていないメッセージについての言及を確認できません。

支援に感謝します-Android-sdkで提供されているデモアプリから逸脱することはほとんどありませんが、必要に応じてソースを提供できます。

39
Seidr

私もこれに気づきました。私は実際のコードを掘り下げていませんが、これがなぜ起こるかについての私の理解があります。

GCM(およびほとんどのプッシュメッセージングサービス)は、Googleのプッシュ通知サーバーに対して長期間有効なソケットを開いたままにすることで機能します。ソケットは、電話とサーバーの間で「ハートビート」メッセージを送信することによって開いたままになります。

場合によっては、ネットワークの状態が変化してこのソケットが壊れることがあります(たとえば、デバイスのIPアドレスが3gからwifiに変わるため)。ソケットが再確立される前にメッセージが届いた場合、デバイスはメッセージをすぐには取得しません。

再接続は、電話がソケットの破損に気付いたときにのみ発生します。これは、ハートビートメッセージを送信しようとしたときにのみ発生します。

繰り返しますが、それがどのように機能し、なぜそれが発生するかについての私の基本的な理解だけで、私は間違っている可能性があります。

36
theelfismike

GCMメッセージの遅延には多くの原因があります。ネットワーク状態を変更した後、または機内モードのオン/オフを切り替えた後にメッセージが届き始めた場合-最も可能性の高い原因は、FIN/RSTを送信せずに接続を閉じるネットワークです。

GCMは長期間有効な接続を維持し、接続が切断されたことがわかっている場合は再接続します。ルーター/ AP/NATは、FINまたはRSTを送信してTCP接続を終了するようになっているため、GCMとサーバーは接続が切断されていることを認識します。

ただし、多くのルーターやモバイルオペレーターはこれを行わず、GCMはハートビートに依存する必要があります。バッテリー寿命/ネットワーク使用とハートビート頻度の間にはトレードオフがあります。

20

上記の回答のコメントのとおり、GCMプッシュ通知に関する私の経験によれば、ネットワーク(インターネット接続)が利用可能である場合、プッシュ通知を受信すべきではない理由はありません。プッシュ通知のアプリケーションを実行する前に、インターネット接続の可用性を常に確認しますこれが本当である場合、これをチェックしてみてくださいプッシュ通知を受け取る必要があります

    private boolean isNetworkAvailable() {
    ConnectivityManager connectivityManager 
          = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
    return activeNetworkInfo != null;
}
1
Sourabh Saldi

したがって、@ theelfismikeの考え方が正しい場合、次のようなものを使用できますか?

  ConnectivityManager cm = (ConnectivityManager) context
            .getSystemService(Context.CONNECTIVITY_SERVICE);

    NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
    if (null != activeNetwork) {
        if(activeNetwork.getType() == ConnectivityManager.TYPE_WIFI)
           // here the network changed to Wifi so I can send a heartbeat to GCM to keep connection

        if(activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE)
          //here the network changed to mobileData so I can send a heartbeat to GCM to keep connection
    }

私の解決策は良いですか?

0