Inicio Android Notificaciones Push en Android: Firebase Cloud Messaging (4)

Notificaciones Push en Android: Firebase Cloud Messaging (4)

por sgoliver

[mensaje-curso]

En los artículos anteriores ya hemos resuelto dos de los problemas planteados en el primer capítulo. Por un lado hemos aprendido a identificar usuarios individuales para poder dirigir mensajes a dispositivos concretos. Por otro, ya tenemos preparada nuestra aplicación para recibir notificaciones tanto cuando no se encuentra visible como cuando es la aplicación en primer plano. En esta cuarta entrega de la serie vamos a centrarnos en el envío y recepción de datos adicionales dentro de los mensajes de Firebase Cloud Messaging.

Hasta ahora, he estado utilizando los términos «notificación» y «mensaje» casi indistintamente, pero para ser exactos debería haber utilizado siempre el concepto de «mensaje de notificación» o «mensaje con carga de notificación» (en inglés, «notification payload«).

Firebase Cloud Messaging distingue entre dos tipos de mensajes:

  1. Mensajes de notificación, o con carga de notificación.
  2. Mensajes de datos, o con carga de datos.

Los mensajes de notificación, que son los que hemos estudiado hasta ahora, son los más limitados, aunque suficientes en multitud de ocasiones. Pueden contener hasta 2 Kb de información, pero distribuida en un conjunto de claves o campos predeterminados. Estos campos predeterminados son los que ya nos deben sonar de artículos anteriores: título, texto, prioridad, sonido, … (existen más campos que los que aparecen en la sección de Notificaciones de la consola de Firebase, más adelante veremos algunos de ellos). El utilizar únicamente campos predefinidos permite al sistema gestionar automáticamente los mensajes en determinadas circunstancias, por ejemplo generando las notificaciones por nosotros cuando la aplicación se encuentra en segundo plano.

Por su parte, los mensajes de datos permiten enviar conjuntos de clave-valor totalmente personalizados hasta un límite de 4 Kb de información. No tendremos que limitarnos a los campos disponibles, sino que podremos definir los nuestros propios según las necesidades de nuestra aplicación. Sin embargo, al no basarse en campos predefinidos, la gestión completa de estos mensajes dependerá exclusivamente de nuestra aplicación, es decir, el sistema no podrá hacer nada por nosotros como en el caso de las notificaciones.

En cualquier caso, es importante entender que estos dos tipos de mensajes no son excluyentes, es decir, se pueden enviar mensajes que contengan ambos tipos de carga, por un lado la información asociada a la notificación y por otro los datos personalizados. En breve veremos cómo y dónde podremos gestionar los mensajes en cada caso.

Como hemos indicado, los mensajes de datos «puros» (es decir, sin carga de notificación) deben ser gestionados en su totalidad por nuestra aplicación. Sin embargo, el lugar donde podremos gestionar estos datos ya lo conocemos, será el mismo servicio de recepción de mensajes (extendido de FirebaseMessagingService) que ya utilizamos en el artículo anterior. En el caso de mensajes de datos puros éste será el único lugar donde se podrá gestionar la información recibida, tanto si la aplicación está visible como en segundo plano.

Dentro del método onMessageReceived() de este servicio podremos acceder a los datos incluidos en el mensaje de forma análoga a como accedíamos a los datos de la carga de notificación, con la salvedad de que usaremos el método getData() en vez de getNotification().

Imaginemos que nuestros mensajes de datos van a incluir por ejemplo dos campos (claves) personalizados para informar del estado de conexión de un usuario. Llamaremos a estos campos «usuario» y «estado». Dentro del método onMessageReceived() podríamos obtener los valores de estas claves obteniendo los datos del mensaje con getData() y llamando posteriormente al método get() pasando como parámetro el nombre de cada clave:

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {

    if (remoteMessage.getNotification() != null) {
        String titulo = remoteMessage.getNotification().getTitle();
        String texto = remoteMessage.getNotification().getBody();

        Log.d(LOGTAG, "NOTIFICACION RECIBIDA");
        Log.d(LOGTAG, "Título: " + titulo);
        Log.d(LOGTAG, "Texto: " + texto);

        //Opcional: mostramos la notificación en la barra de estado
        showNotification(titulo, texto);
    }

    if(remoteMessage.getData() != null) {
        Log.d(LOGTAG, "DATOS RECIBIDOS");
        Log.d(LOGTAG, "Usuario: " + remoteMessage.getData().get("usuario"));
        Log.d(LOGTAG, "Estado: " + remoteMessage.getData().get("estado"));
    }
}

Como podéis ver hemos dejado también la parte de gestión de notificaciones que ya implementamos en el artículo pasado porque como hemos indicado es posible recibir mensajes que contengan ambos tipos de información (notificación + datos personalizados).

Para probar si todo funciona correctamente vamos a utilizar una vez más la consola de Firebase, pero nos encontraremos con un pequeño problema. Si nos fijamos bien, el campo «Texto del mensaje» es obligatorio en el formulario de envío de mensajes. Este campo es propio de los mensajes de notificación por lo que acabamos de descubrir que la consola de Firebase no soporta el envío de mensajes de datos puros, sino tan sólo de mensajes de notificación o de mensajes «mixtos», es decir, con carga simultánea de notificación y de datos. Este problema viene a agravar la cuarta de las limitaciones que encontramos en el primer artículo de la serie sobre Firebase Cloud Messaging, es decir, la Consola de Firebase puede resultar muy práctica para el envío de mensajes en multitud de ocasiones, pero puede no adaptarse a nuestras necesidades en muchas otras, por lo que es necesario contar con otra alternativa (lo que veremos en un próximo artículo).

Por ahora nos conformaremos con enviar mensajes que contengan tanto notificación como datos desde la consola. Para añadir datos personalizados a un mensaje basta con indicar los pares de clave y valor en las opciones avanzadas del formulario de envío. Por seguir con nuestro ejemplo podríamos añadir las claves «usuario» y «estado» con dos valores de ejemplo:

fcm-consola-datos-personalizados

Si ejecutamos la aplicación en este momento, dejándola en primer plano, y enviamos el mensaje con los datos adicionales tal como se muestra en la imagen anterior, podremos ver en el log del sistema como se reciben correctamente los datos en el método onMessageReceived()

fcm-log-datos-recibidos

Podemos observar también como justo encima de los datos recibidos aparece también la información asociada a la notificación, lo que nos confirma que la Consola de Firebase no es capaz de enviar mensajes de datos sin carga de notificación (al menos en el momento de escribir este tutorial).

Aparentemente ya estaría todo resuelto con los mensajes de datos, pero aún nos queda un problema más por solucionar. Vamos a volver a enviar otro mensaje con datos personalizados desde la consola, pero esta vez con nuestra aplicación en segundo plano. ¿Qué ha ocurrido? El sistema ha generado automáticamente una notificación en la bandeja del sistema (por recibirse carga de notificación pero no encontrarse la aplicación en primer plano), pero sin embargo no se ha ejecutado el método onMessageReceived(), por lo que no hemos podido obtener los datos personalizados del mensaje.

Esto no es ningún error, es el funcionamiento esperado del sistema en estos casos. En el caso de mensajes mixtos (de notificación + datos), cuando la aplicación se encuentra en segundo plano el sistema gestionará por nosotros la información de notificación mostrando automáticamente el mensaje en la bandeja del sistema, pero condicionará la entrega a nuestra aplicación de los datos personalizados a que el usuario pulse sobre la notificación mostrada. Si el usuario pulsa sobre la notificación los datos se recibirán como extras del intent que iniciará nuestra aplicación. Podremos obtener estos datos por ejemplo en el método onCreate() llamando a getIntent() y sobre éste a getExtras().

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (getIntent().getExtras() != null) {
        Log.d(LOGTAG, "DATOS RECIBIDOS (INTENT)");
        Log.d(LOGTAG, "Usuario: " + getIntent().getExtras().getString("usuario"));
        Log.d(LOGTAG, "Estado: " + getIntent().getExtras().getString("estado"));
    }

    //...
}

Si volvemos a hacer nuevamente la prueba anterior, enviando el mensaje con datos cuando nuestra aplicación esté en segundo plano, ahora sí deberíamos ver en el log los datos recibidos al pulsar sobre la notificación generada automáticamente por el sistema.

fcm-log-datos-recibidos-intent

Es importante entender que si hubiéramos podido enviar mensajes de datos puros desde la consola no habría sido necesario hacer esto último, ya que los mensajes que únicamente incluyen carga de datos siempre se reciben en el método onMessageReceived(), independientemente de que la aplicación esté visible o en segundo plano.

A modo de resumen, la siguiente tabla muestra cuándo y donde se recibe la información de un mensaje dependiendo de su tipo y la situación actual de nuestra aplicación.

Situación Aplicación Notificación Datos Notificación + Datos
En primer plano onMessageReceived() onMessageReceived() onMessageReceived()
En segundo plano Bandeja del sistema onMessageReceived() Notificación: Bandeja del sistema
Datos: Extras del intent

Con esto finalizaríamos el apartado dedicado a los mensajes de datos y tan sólo nos quedaría por ahora buscar una alternativa a la consola de Firebase, que sea más completa, versátil e integrable con el resto de nuestro sistema. Lo veremos en el próximo artículo.

Puedes consultar y/o descargar el código completo de los ejemplos desarrollados en este artículo accediendo a la página del curso en GitHub.

[mensaje-curso]

También te puede interesar

16 comentarios

carlos 26/01/2017 - 1:56

hola. sabes mandar notificaciones por código? y si no, sabes de algún tutorial para aprender?

Responder
ericdoom 30/01/2017 - 16:57

Excelente material amigo, sigue asi!!!

Responder
Alvaro Neira 24/02/2017 - 6:02

Excelente, gran tutorial, he visto todos tus tutoriales y son lejos los mejores que he encontrado. Respecto al tema de este post, mi duda es como gestionar por nosotros mismos las notificaciones en segundo plano y que no se gestionen automáticamente por la bandeja del sistema, por ejemplo para personalizar el icono.

Responder
Fernando Garcia 07/03/2017 - 22:07

Excelente tutorial, como siempre.

Tengo una duda. Segun FCM la carga de datos es de hasta 4k, ¿como puedo calcular o saber si me excedo de esos 4k? ¿Hay alguna manera?

Responder
Debbie 16/03/2017 - 3:28

Buenísimo como siempre muchas gracias por la ayuda!!! :D

Responder
erick 03/04/2017 - 17:36

buen tutorial, pero para cuando estara el capitulo 5?

Responder
oscar 07/04/2017 - 0:59

que facil y practico, sigan asi

Responder
Carlos 11/04/2017 - 3:24

Esperando el nuevo tutorial! amigo muy buenos los anteriores :)

Responder
Arriaga 21/06/2017 - 22:31

Como envio mensajes desde otro terminal o codigo

Responder
Joan Martínez 01/01/2018 - 9:45

Hola,

Estoy finalizando una app con mensajería y compruebo que solo en determinados modelos de teléfono (Huawei preferentemente) cuando cancelas voluntariamente la app se cancela el servicio y así la capacidad de recibir mensajes.

Se que este problema no lo tengo si activo manualmente el indicador de «aplicación protegida» en la configuración del teléfono y por ello me gustaría saber como podría a nivel de permisos declarados en la app activar este indicador justo en la instalación.

Esto lo hacen apps como WhatsApp, Twitter, Instagram, …

Muchísimas gracias por anticipado.

Responder
julius 10/03/2018 - 0:12

Quisiera saber si tienes alguna app, para mandar notificaciones a un tema, pero desde la app y no la consola para poder manipular los mensages que se reciben. Muchas gracias por tu tutorial, de antemano espero me puedas ayudar.

Responder
Kenny 20/04/2018 - 23:56

Hola, excelente tutorial, para cuando el siguiente capitulo?, gracias !

Responder
Aitor 26/04/2018 - 23:02

Hola muy buen tutorial me ha ayudado mucho pero tengo una duda yo uso un servidor en php para enviar las notificaciones y cuando la app esta cerrada o en segundo plano al hacer click en la notificaión no me abre la app, he usado el getData() como indicas para recoger el valor de payload.
¿alguna idea ?
gracias

Responder
Santiago 02/05/2018 - 23:00

Pasa todo un día buscando una solución óptima hasta que encontré tu post gracias amigo (y)

Responder
Mateo 04/09/2018 - 15:05

Buenos días, estoy trabajando sobre este tema con dos dispositivos físicos. Un Galaxy J5 y un J7. Las notificaciones aparecen perfectamente en el J7 no asi en el j5. La notificación aparece y desaparece muy rápido. es casi imperceptible al ojo.
Me podrías decir a que se puede deber?

Saludos

Responder
Cristhian Lagla 15/04/2019 - 0:06

Buenas quisiera saber como puedo hacer una notificación en mi app cuando la base de datos de firebase se agregue un nuevo dato.

Responder

Dejar un comentario

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información. Aceptar Más Información

Política de Privacidad y Cookies