Google Cloud Messaging: a veces los mensajes no se reciben hasta que cambia el estado de la red


Mientras trabajaba en un pequeño proyecto que se integra con GCM, me he topado con un problema un poco extraño.

Algunas veces cuando empiezo a ver el registro para ver si se reciben mensajes, los mensajes no parecen estar llegando hasta que he cambiado el estado de la red (Es decir, originalmente en WiFi, si apago WiFi y me muevo a Datos móviles, los mensajes llegan bien). Después de haber cambiado el estado de la red, los mensajes comienzan a llegar perfectamente bien, y lo mismo se aplica una vez que cambio el estado de red de nuevo a lo que era antes (en este caso, WiFi) los mensajes siguen siendo recibidos.

El proyecto en sí incluye la capacidad de iniciar en el arranque (inicia el GCMBaseIntentService en el arranque), que de nuevo funciona perfectamente bien, y estoy seguro de que la aplicación / servicio se está ejecutando ya que he iniciado manualmente la aplicación cuando se produce este problema (que también comprueba si el servicio se está ejecutando, y si no lo está, lo ejecuta y comprueba si está registrado).

¿Ha venido alguien más? a través de este problema, o tiene algún consejo en cuanto a cómo podría resolver esto? No veo nada de mucha ayuda en el registro entre el momento en que los mensajes no se reciben y cuando lo son (después de cambiar el estado de la red). He revisado los documentos de GCM y no puedo ver ninguna mención de mensajes que no se reciben debido a un tiempo de espera (en el propio dispositivo), o cualquier opción de configuración que pueda afectar esto.

Agradezco cualquier ayuda - puedo proporcionar la fuente si es necesario, aunque apenas se desvía desde la aplicación de demostración proporcionada en el android-sdk.

Author: Seidr, 2012-12-12

5 answers

He notado esto también. Aunque no he profundizado en el código real, aquí está mi comprensión de por qué sucede esto.

GCM (y la mayoría de los servicios de mensajería push) funciona manteniendo un socket de larga duración abierto al servidor de notificaciones push de Google. El socket se mantiene abierto enviando mensajes "heartbeat" entre el teléfono y el servidor.

De vez en cuando, el estado de la red puede cambiar y este socket se romperá (porque la dirección IP del dispositivo cambia, de 3g a wifi, para ejemplo). Si el mensaje llega antes de que se restablezca el socket, entonces el dispositivo no recibirá el mensaje inmediatamente.

La reconexión solo ocurre cuando el teléfono nota que el socket está roto, lo que solo sucede cuando intenta enviar un mensaje de latido.

De nuevo, solo mi comprensión básica de cómo funciona y por qué sucede, y podría estar equivocado.

 36
Author: theelfismike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-13 18:49:28

Hay muchas causas para los retrasos de mensajes de GCM. Si el mensaje comienza a llegar después de cambiar el estado de la red o de activar/desactivar el modo avión, la causa más probable es una red que cierra la conexión sin enviar FIN/RST.

GCM mantiene una conexión de larga duración y se vuelve a conectar si sabe que la conexión se ha roto. Se supone que un router/AP/NAT debe enviar una FIN o RST para terminar la conexión TCP, por lo que GCM y los servidores sabrán que la conexión está muerta.

Sin embargo, número de routers y operadores móviles no hacen esto, y luego GCM necesita confiar en el latido del corazón, ~15 min en Wifi, más en móvil. Hay un equilibrio entre la duración de la batería/el uso de la red y la frecuencia del latido del corazón.

 19
Author: Costin Manolache,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-16 20:14:43

Según su comentario en la respuesta anterior y de acuerdo con mi experiencia con las notificaciones push de GCM, no hay ninguna razón por la que si la red(conexión a Internet) está disponible, no debe recibir notificaciones push Siempre compruebo la disponibilidad de la conexión a Internet antes de ejecutar la aplicación para notificaciones push como esta intente verificar esto si esto es cierto, debe recibir notificaciones push

    private boolean isNetworkAvailable() {
    ConnectivityManager connectivityManager 
          = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
    return activeNetworkInfo != null;
}
 1
Author: Android Fanatic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-12 16:44:23

Entonces, si el pensamiento de @theelfismike es cierto, puedo usar algo como:

  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
    }

¿Es buena mi solución?

 0
Author: Esmaeel Ibraheem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-07 17:37:30

En la clase GCMIntentSevice ,cuando recibe un mensaje del servidor ,se llama al método onMessage, por lo que en ese momento puede hacer algo como...

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();

Y hay que añadir

<uses-permission android:name="android.permission.WAKE_LOCK" /> permiso en tu archivo de manifiesto..

Esto debería hacer el truco...

 -4
Author: user1918096,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-09 08:43:56