Google Cloud Messaging - сообщения, которые иногда не принимаются, пока не изменилось состояние сети
Во время работы над небольшим проектом, который интегрируется с GCM, я наткнулся на немного странную проблему.
Несколько раз, когда я начинаю наблюдать за журналом, чтобы узнать, получены ли сообщения, сообщения не появляются до тех пор, пока я не изменил состояние сети (IE первоначально на WiFi, если я отключу WiFi и перейду к Mobile Data, сообщения поступают нормально). После того, как я изменил состояние сети, сообщения начнут поступать совершенно нормально, и то же самое произойдет, как только я изменю состояние сети до того, что было раньше (в данном случае, WiFi), сообщения продолжают получать сообщения.
Сам проект включает в себя возможность запускать при загрузке (запускает GCMBaseIntentService при загрузке), что опять отлично работает, и я уверен, что приложение/служба запущено, поскольку я вручную запустил приложение, когда эта проблема (который также проверяет, работает ли служба, и если она не запускает его и проверяет, зарегистрирована ли она).
Кто-нибудь еще сталкивается с этой проблемой или имеет какие-либо указания относительно того, как я могу это решить? Я не вижу ничего полезного в журнале между сообщениями времени, которые не принимаются и когда они (после изменения состояния сети). Я прошел через документы GCM и не вижу никаких упоминаний о сообщениях, которые не принимаются из-за тайм-аута (самого устройства) или любых параметров конфигурации, которые могут повлиять на это.
Оцените любую помощь - я могу предоставить источник, если это необходимо, хотя он вряд ли отклоняется от демонстрационного приложения, представленного в android-sdk.
Ответы
Ответ 1
Я тоже это заметил. Хотя я не врывался в фактический код, здесь я понимаю, почему это происходит.
GCM (и большинство служб обмена сообщениями) работает, поддерживая долгоживущий сокет, открытый для сервера push-объявлений Google. Сокет остается открытым, отправляя сообщения "heartbeat" между телефоном и сервером.
Иногда состояние сети может меняться, и этот сокет будет поврежден (потому что IP-адрес устройства изменяется, например, с 3g на Wi-Fi). Если сообщение появляется до того, как сокет будет восстановлен, то устройство не получит сообщение немедленно.
Повторное подключение происходит только тогда, когда телефон уведомляет о сбое сокета, что происходит только тогда, когда он пытается отправить сообщение с пульсом.
Опять же, просто мое основное понимание того, как это работает и почему это происходит, и я мог ошибаться.
Ответ 2
Существует множество причин задержки сообщений GCM. Если сообщение начнет поступать после изменения состояния сети или включения/выключения режима полета - наиболее вероятной причиной является сеть, которая закрывает соединение, не отправляя FIN/RST.
GCM поддерживает долговременное соединение - и восстанавливается, если он знает, что соединение было нарушено.
Предполагается, что маршрутизатор /AP/NAT отправит FIN или RST для завершения TCP-соединения - так что GCM и серверы будут знать, что соединение мертво.
Однако количество маршрутизаторов и мобильных операторов этого не делает, и тогда GCM нужно полагаться на сердцебиение, ~ 15 минут на Wi-Fi, больше на мобильных устройствах. Существует компромисс между временем автономной работы/использованием сети и частотой пульса.
Ответ 3
В соответствии с вашим комментарием в приведенном выше ответе и в соответствии с моим опытом с уведомлениями о push-канале GCM нет никакой причины, что если сеть (подключение к Интернету) доступна, вы не должны получать push-уведомления. Я всегда проверяю наличие подключения к Интернету перед запуском приложения для push-уведомления, как это, попробуйте проверить это, если это правда, вы должны получать push-уведомления
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager
= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null;
}
Ответ 4
Итак, если @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
}
Мое решение хорошее?
Ответ 5
в классе GCMIntentSevice, когда вы получаете сообщение от сервера, метод onMessage вызывается так, что в это время вы можете сделать что-то вроде...
PowerManager pm = (PowerManager) getApplicationContext()
.getSystemService(Context.POWER_SERVICE);
WakeLock wakeLock = pm.newWakeLock(
(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
| PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP),"TAG");
wakeLock.acquire();
и вам нужно добавить
<uses-permission android:name="android.permission.WAKE_LOCK" />
разрешение в вашем файле манифеста.
это должно сделать трюк...