Notificaciones push con Android, Google Cloud Message y JEE

6
31116

Notificaciones push con Android, Google Cloud Message y JEE

0. Índice de contenidos.

1. Entorno

Hardware: portátil Macbook Air (Intel Core I7, 8GB) y un Nexus 5 (este tutorial solo se ha probado con un dispositivo físico, no hay garantías que funcione con un dispositivo virtual)

Sistema operativo: OS X 10.9.3 y Android 4.4.4
JDK 1.7.0.51
Apache Tomcat 7.0.52
Eclipse Luna (lado del servidor)
Eclipse ADT Juno (lado del cliente Android)
Apache Maven 3.2.1

2. Introducción

Una notificación push es un tipo de comunicación entre un dispositivo cliente y un servidor en el que es este último es el que inicia la petición, es decir, el servidor notifica al dispositivo cliente sobre algún evento sin que el usuario final tenga que realizar acción alguna. Esto se entiende mejor con el ejemplo de la aplicación de gmail en Android que nos alerta mediante una notificación visual y/o sonora de la llegada de nuevos emails incluso aunque la aplicación en si no la hayamos abierto o el terminal este en “stand by” y bloqueado.

Las notificaciones push tienen como ventaja frente a la técnica del polling (peticiones periódicas al servidor para averiguar si hay nuevos eventos pendientes de notificar) que consume menos recursos y las notificaciones llegan al instante y no al cabo de un periodo de tiempo.

En este tutorial veremos como podemos implementar una notificación push enviada desde una aplicación jee a una aplicación Android usando el servicio gratuito GCM (Google Cloud Messaging ) que es proporcionado por Google.
Para ilustrar todo el proceso se va hacer una pequeña aplicación jee a través de la cual se puede mandar una notificación a una determinada aplicación cliente de un terminal Android en concreto, con un mensaje introducido en un formulario de dicha aplicación.

En la imagen de abajo se ve un simple formulario web en el que se introduce el mensaje de la notificación y un botón para enviarlo

Al pulsar el botón del formulario se envía el mensaje a los servidores de GCM y se muestra un simple página de confirmación

Una vez el mensaje ha llegado a los servidores GCM este lo reenvía al dispositivo Android de prueba que mostrará la notificación en la barra de notificaciones con el mensaje que hemos introducido en el formulario web, incluso si la aplicación cliente no esta abierta.

Para el uso de GCM intervienen tres actores, el dispositivo Android que recibirá la notificación, el servidor en el que se ejecuta el Tomcat, y los servidores de GCM proporcionados por Google. En el esquema que se presenta a continuación se puede ver que el proceso de notificación consta de 5 pasos enumerados por orden y que se describe con detalle.

  • Paso 1: La aplicación instalada en nuestro terminal Android se registra enviando a los servidores GCM el “Sender ID” y el “Application ID”. El “Sender ID” es el identificador de la instancia del paquete de servicios de “Google Play Services” entre los cuales se encuentra GCM y que obtendremos en la pagina de “Google Apis Console”, y finalmente la “Application ID” es el identificador de la aplicación formado a partir del nombre del paquete del mismo.
  • Paso 2: Si el proceso de registro se ha realizado correctamente los servidores de GCM devolverán un “Registration ID” a la aplicación móvil. El Registration ID es un identificador que identifica una aplicación concreta en un dispositivo concreto.
  • Paso 3: Reenviamos el “Registration ID” desde la aplicación Android hacia nuestro servidor en el cual se ejecuta el Tomcat y que guardará dicho identificador para el siguiente paso.
  • Paso 4: Se manda el mensaje desde nuestro servidor a los servidores de GCM junto a el “Registration ID” que indica el destinatario del mensaje y que hemos guardado previamente en el paso 3, y finalmente el “Sender Autch Token” que permite autentificarnos contra una determinada instancia del paquete de servicios de “Google Play Services”.
  • Paso 5: Los servidores de GCM manda el mensaje a los dispositivos destinatarios y se visualiza en la barra de notificaciones.

3.- Registro

La aplicación cliente del terminal Android se debe de registrar en el servicio de GCM, para ello se debe primero activar y configurar una instancia de dicho servicio desde la consola de Apis de Google a través de la siguiente URL: https://code.google.com/apis/console

Luego se crea un proyecto nuevo, a no ser que ya tengamos uno creado. En este contexto , un proyecto es una instancia del paquete de servicios que Google ofrece bajo el nombre de “Google Play Services”.

Una vez creado el proyecto, se va a “Mi proyecto”/MONITORING/Overview y se visualiza el “Project Number” (en la captura de abajo se muestra borroso) que se copiara en alguna parte ya que es necesario mas adelante.

Una vez el Project Number esta guardado en alguna parte, se ira a “TU proyecto”/APIS&AUTH/APIs donde se visualiza un listado de todos los servicios disponibles en la Google Play Services, se activa el servicio “Google Cloud Messaging for Android”

Ya solo falta generar el server Key, se va a “Mi proyecto”/APIS&AUTH/Credentials y se hace click en “Create new Key”. En la siguiente pantalla se ve que ya existe un “Key for server application” creado previamente pero en realidad no debe existir todavía en este paso.

Y luego se hace click en el botón Server Key

En la siguiente pantalla esta la opción de restringir la IP desde la cual el servidor con el Tomcat puede acceder al servicio GCM, si no se pone nada será posible acceder a dicho servicio desde cualquier IP. En este ejemplo se va a dejar vacío.

Después de que se ha creado el “Server key” copiamos al “Api key” en alguna parte ya que se necesitara mas adelante en el paso 5.

Ahora que ya se ha creado, activado y configurado el servicio GCM, el siguiente paso es crear un nuevo proyecto Android que hace de cliente y configurar la “Google Play Services” para que podamos invocar al servicio de registro.

En la siguiente pantalla se puede ver la estructura de la aplicación cliente ya finalizada.

Para configurar la “Google Play Services” en nuestro proyecto debemos primero descargarnos la librería pertinente desde Android SDK Manager, concretamente en Extras/Google Play Services

Una vez ya se ha descargado, se puede encontrar la librería en /extras/google/google_play_services/libproject/google-play-services_lib la cual se importa al workspace.

Para los que no estén familiarizado con el entorno de Android he decir que la forma en que se añaden librerías a un proyecto Android difiere con una aplicación jee, así que tomad buena nota de los siguientes pasos.

Para importar la librería se va a File -> Import -> Android -> Existing Android Code into Workspace y se busca la carpeta mencionada justo arriba.

Una vez indicada la ubicación de la librería, marcamos el check “Copy proyects into workspace” y se hace click en el botón Finish.

Ahora se asocia la librería importada al proyecto Android que hace de cliente, para ello
se hace click en el botón derecho sobre la raíz del proyecto, y en Properties->Android aparece una pantalla como la de abajo,primero en “Proyect Build Target” seleccionamos Google APIs y en segundo lugar, en el apartado “Library” se hace click en el botón Add para asociar una nueva librería

luego se selecciona google-play-services_lib y se hace click en OK

Y ya tendríamos la librería añadida a nuestro proyecto, ahora es preciso añadir algunas configuraciones para poder invocar los servicios de GCM. En el AndroidManifest.xml añadimos dentro de la etiqueta <application>

Y dentro de la tag <manifest> añadimos los siguientes permisos

Y en el fichero proguard-project.txt ubicado en la raíz del proyecto Android añadimos la siguiente configuración después de la última linea del mismo:

Ahora que esta instalado en el proyecto la librería de Google Play Service, se procede a explicar el código de la aplicación. Para empezar la interfaz de la actividad principal, en este ejemplo MainActivity no tiene nada, salvo un mensaje al iniciarse la misma. Esto es así porque para recibir una notificación lo único necesario es que la aplicación se ejecute una primera vez para se efectúe el registro, luego aunque la aplicación no este abierta se recibirá la notificación de todas formas.

El layout /DemoGCM/res/layout/activity_main.xml queda asi:

Y el fichero de literales /DemoGCM/res/values/strings.xml queda así:

En el metodo onCreate() de la actividad principal, en este caso “/DemoGCM/src/com/goplasoft/demogcm/MainActivity.java”, verificamos que Google Play Services esta operativo y si es así se procede con el registro.

Ahora se explica el código paso a paso, lo primero se declara las siguientes variables a nivel de clase del Activity:

La constante SENDER_ID debe de inicializarse con el “Project Number” que aparece en la Api console y que se dijo que se copiara en alguna parte previamente en este tutorial.
En cuanto a la URL_REGISTRO_ID se tiene que reemplazar las X por la ip y puerto del servidor Tomcat.

Advertencia : Si se pone “localhost” y se prueba con un dispositivo Android físico no funciona ya que el Tomcat no esta instalado en el propio terminal Android sino en una servidor externo.

En el metodo onCreate() pondremos lo siguiente:

En el primer if hay un método que se encarga de chequear que el paquete de Servicios de Google Play Services esta disponible, su código es el siguiente:

Posteriormente se intenta obtener el identificador de registro almacenado en el dispositivo en caso que dicho identificador haya sido cacheado, el código que hace esto es el siguiente:

Si el identificador de registro no estuviese previamente almacenado entonces es necesario realizar el proceso de registro para obtener dicho identificador que se almacena en el propio dispositivo Android a modo de caché y también se envía al servidor Tomcat a través de un servicio REST. Mas adelante se ve como se implementa dicho servicio REST. El código que hace todo lo descrito es:

Advertencia: Es posible que cuando se ejecute el método de registro:

Se devuelva un error “service not available”, este es un error muy genérico que puede tener múltiples causas, para solucionarlo prueba con verificar que el reloj del terminal Android esta en hora, revisar los permisos y quitar los puntos de ruptura ubicados previamente o en esa misma linea de código si se esta ejecutando en modo debug.

4.- Reenvío del “Registation ID” al servidor Tomcat

En este paso se envía el identificador de registro que esta en posesión del cliente Android a la aplicación jee para que esta última pueda indicar al servicio de GCM el destinatario del mensaje de la notificación. El envío de dicho identificador se hace invocando un servicio REST por parte de la aplicación Android y cuya implementación esta hecha en la aplicación jee que en este ejemplo he llamado “gcmserver”.

En el método registroEnSegundoPlano descrito en el paso 3 se invoca a otro método enviarIdentificadorRegistroALaAplicacionJ2ee donde esta el cliente del servicio REST. Dicho servicio se hace por método post y recibe los parámetros envueltos por un objeto JSON y este su vez responde con un mensaje que también esta envuelto en otro objeto JSON. A continuación se muestra el código del cliente del servicio:

Ahora se procede a explicar la implementación del servicio REST en la parte del servidor. Para ello se ha utilizado JAX-RS y en concreto la implementación Jersey proporcionada por Oracle. La aplicación jee sigue la estructura estándar de Maven tal como se muestra en el siguiente captura:

Lo primero de todo es necesario añadir las dependencias de jersey en nuestro fichero /gcmserver/pom.xml, que son:

Y las propiedades a añadir son:

También tendremos que añadir el repositorio:

Para poder empezar a usar Jersey se tiene que añadir las siguientes lineas en el fichero /gcmserver/src/main/webapp/WEB-INF/web.xml

El servlet recibe un parámetro cuya clave es “jersey.config.server.provider.packages” y su valor será el paquete donde dicho servlet empezará a escanear las clases en búsqueda de alguna que esté anotada con las anotaciones de JAX-RS. Se deberá por tanto adaptar la siguiente linea código:

A continuación se ve el código del servicio que lo único que hace es guardar el identificador de registro pasado por parámetro en un fichero de texto plano. Normalmente cada uno de los identificadores de registro de los múltiples clientes Android se almacenaría y gestionaría en una BBDD pero por motivos didácticos y para hacer el ejemplo mas simple no se utiliza ninguna.

La URL para invocar el servicio REST es:

Donde webapi esta definido en el web.xml, “registration/id” esta definido en la anotación @Path(«registration/id») a nivel de clase y finalmente /add en la anotación @Path(«/add») a nivel de método. A continuación se muestra el código:

5.- Envío del mensaje a los servidores GCM

En este paso se crea un formulario web donde se podrá introducir el mensaje de la notificación y enviar dicho mensaje al servicio de GCM que su vez lo reenviará al dispositivo Android correspondiente. Para ello se utiliza un par de jsp con JSTL y un servlet de los de toda la vida.

Lo primero es añadir las dependencias de Gson y JSTL en el pom.xml

El /gcmserver/pom.xml al completo debe quedar así:

A continuación se crea la primera JSP “/gcmserver/src/main/webapp/index.jsp” que se visualiza inicialmente y que contiene el formulario web donde se introduce el mensaje de la notificación:

Luego se crea el servlet EnviarMensajePush, dicho servlet se encarga de recoger del formulario web el mensaje de la notificación y reenviarlo al servicio REST de GCM para que este a su vez se lo mande al dispositivo Android pertinente.

Para invocar al servicio REST es necesario realizar la petición http por método post y además pasar una serie de parámetros tanto en la cabecera de la petición como en su cuerpo de la misma:

En la cabecera de la petición debemos indicar los siguientes parámetros:

En “Authorization” debemos reemplazar las X por la “ Api key” que se obtuvo de la “api console” en el paso 3 de este tutorial, y en el cual se dijo que se guardara justo después de haber creado el “Server key”.

En el cuerpo de la petición http pasaremos un objeto json que tendrá al menos los atributos “registration_ids” y “data” con una estructura similar a esta:

“registration_ids” es un array de identificadores de registro, uno por cada dispositivo Android destinatario de la notificación, en este tutorial, como solo hay un único dispositivo destinatario de prueba el array contendrá un único identificador.

«data» es un objeto json que contiene el conjunto de datos de la notificación y cuya estructura no esta definida de antemano.

Ademas de los atributos obligatorios hay otros que son opcionales que en este tutorial no se van a utilizar pero no esta de más echar un vistazo:

Atributo Descripción
notification_key Una cadena que se asigna a un solo usuario con múltiples ID de registro asociados con dicho usuario. Esto permite que un servidor de 3 ª parte pueda enviar un mismo mensaje a varias instancias de la aplicación (por lo general en varios dispositivos), propiedad de un único usuario. Un servidor tercero-partido puede utilizar notification_key como destino de un mensaje en lugar de un ID de registro individual (o matriz de identificadores de registro). El número máximo de miembros permitidos para un notification_key es 10.
collapse_key Una cadena arbitraria (como «Actualizaciones disponibles») que se utiliza para contraer un grupo de mensajes como cuando el dispositivo está en línea, por lo que sólo el último mensaje se envía al cliente. Con ello se pretende evitar enviar demasiados mensajes en el teléfono cuando este vuelve de nuevo a estar en linea. Tenga en cuenta que, dado que no hay ninguna garantía del orden en que los mensajes son enviados, el mensaje «último» en realidad no puede ser el último mensaje enviado por el servidor de aplicaciones.
delay_while_idle Si se incluye, indica que el mensaje no debe ser enviado de inmediato si el dispositivo está inactivo. El servidor esperará a que el dispositivo se active, y luego será enviado sólo el último mensaje para cada valor collapse_key. El valor predeterminado es falso, y debe ser un booleano JSON.
time_to_live Valor numérico que indica el tiempo en segundos por el cual debe mantenerse el mensaje en el almacenamiento de GCM si el dispositivo esta fuera de linea, por defecto esta puesto a 4 semanas
restricted_package_name Una cadena que contiene el nombre del paquete de la aplicación. Cuando se establece, sólo se enviarán los mensajes de ID de registro que coinciden con el nombre de paquete.
dry_run Si se incluye, permite a los desarrolladores probar su solicitud sin tener que enviar un mensaje. El valor predeterminado es falso, y debe ser un booleano en el objeto JSON.

A continuación se muestra el código del servlet EnviarMensajePush

Luego se registra en el web.xml el servlet EnviarMensajePush que gestiona la petición del formulario web descrito previamente de la siguiente forma y dentro de la etiqueta :

El /gcmserver/src/main/webapp/WEB-INF/web.xml al completo debería quedar de la siguiente forma:

Y ahora se crea la segunda JSP “/gcmserver/src/main/webapp/confirmacion.jsp” que muestra una confirmación de envío del mensaje una vez el servlet EnviarMensajePush ha enviado el mensaje de la notificación al servicio de GCM. El código de la jsp es el siguiente:

6.- Recepción de la notificación por parte del dispositivo.

En este momento, desde la aplicación web ya se puede enviar notificaciones a los servidores de GCM, ahora solo queda preparar la aplicación cliente para recibir la notificación que GCM reenvía al dispositivo Android. Para ello es necesario crear un BroadcastReceiver que hereda de WakefulBroadcastReceiver y que se encarga de capturar el intent com.google.android.c2dm.intent.RECEIVE que se produce cuando hay una notificación a la espera de ser tratada.
El código del BroadcastReceiver es el siguente:

También es necesario declarar explícitamente el BroadcastReceiver en el AndroidManifest.xml para así poder capturar el intent aunque la aplicación no este activa.
A continuación se muestra las lineas de código que hay que añadir dentro de la tag <application>, después de hacer las adaptaciones pertinentes con el nombre de los paquete .

Ahora se crea el servicio que se encarga de recuperar los datos de la notificación del intent que recibe por parámetro desde el BroadcastReceiver para luego mostrarlos en la barra de notificaciones. El codigo del servicio es:

Solamente falta declarar el servicio en el AndroidManifest.xml con la siguiente linea de código dentro de la etiqueta <application> después de hacer las adaptaciones pertinentes con los nombre de los paquete.

Al final el AndroidManifest.xml debe quedar de manera similar a este:

Y esto esto todo, ya se puede probar si llega la notificación al terminal Android, hay que tener en cuenta que a veces no es algo inmediato y que puede durar unos cuantos segundos.

6 Comentarios

  1. genial el post!! eh me podria decir como mando las notificaciones push de una app(cliente) a otra app(servidor), con nodejs por medio usando socket.io. gracias!!

  2. una pregunta despues de felicitarte por el tuto.. este tema tiene algun valor, ya que hemos implementado pero al enviar seguido unas 5 notificaciones ya no llegan o tienes que esperar de 2 a 3 minutos para que llegue otra…saludos y gracias

Dejar respuesta

Please enter your comment!
Please enter your name here