[box type=»alert» border=»full»]Este artículo ha quedado obsoleto con la aparición de una nueva versión de la API de Google Cloud Messaging. Tienes disponible un nuevo artículo del curso donde aprender a utilizar esta nueva versión.[/box]
En los dos anteriores (I y II) artículos del curso hemos hablado sobre el servicio Google Cloud Messaging y hemos visto como implementar una aplicación web que haga uso de dicho servicio para enviar mensajes a dispositivos Android. Para cerrar el círculo, en este nuevo artículo nos centraremos en la aplicación Android cliente.
Esta aplicación cliente, como ya hemos comentado en alguna ocasión será responsable de:
- Registrarse contra los servidores de GCM como cliente capaz de recibir mensajes.
- Almacenar el «Registration ID» recibido como resultado del registro anterior.
- Comunicar a la aplicación web el «Registration ID» de forma que ésta pueda enviarle mensajes.
- Recibir y procesar los mensajes desde el servidor de GCM.
Para las tareas 1 y 4 utilizaremos una librería específica de GCM que nos proporciona Google para facilitarnos la vida como desarrolladores (es posible hacerlo sin el uso de librerías externas, aunque requiere más trabajo). El punto 2 lo resolveremos fácilmente mediante el uso de SharedPreferences. Y por último el punto 3 lo implementaremos mediante la conexión al servicio web SOAP que creamos en el artículo anterior, sirviéndonos para ello de la librería ksoap2, tal como ya describimos en los artículos sobre servicios web SOAP en Android.
Durante el artículo construiremos una aplicación de ejemplo muy sencilla con el siguiente aspecto:
En esta aplicación, el usuario podrá introducir un nombre de usuario identificativo y pulsar el botón «Aceptar» para que quede guardado en las preferencias de la aplicación. Tras esto podrá registrarse como cliente capaz de recibir mensajes desde GCM pulsando el botón «Registrar GCM». En caso de realizarse de forma correcta este registro la aplicación enviará automáticamente el Registration ID recibido y el nombre de usuario almacenado a la aplicación web a través del servicio web. Igualmente el usuario podrá des-registrarse en el servicio GCM para no recibir más mensajes pulsando el botón «Des-registrar GCM». Obviamente todo este proceso de registro y des-registro debería hacerse de forma transparente para el usuario de una aplicación real, en esta ocasión he colocado botones para ello sólo por motivos didácticos.
Antes de nada vamos a preparar nuestro proyecto de Eclipse y vamos a configurar convenientemente el AndroidManifest para poder hacer uso del servicio GCM y su librería auxiliar.
Para ello vamos a crear un nuevo proyecto Android sobre un target de Android 2.2 o superior que incluya las librerías de Google, y vamos a incluir en su carpeta /libs las librerías de ksoap2 (esto ya vimos como hacerlo en el artículo sobre servicios web) y la librería cliente de GCM llamada «gcm.jar«. ¿Cómo podemos obtener esta librería? Para conseguirla debemos instalar desde el Android SDK Manager el paquete extra llamado «Google Cloud Messaging for Android Library».
Una vez instalado podemos ir a la ruta «RAIZ_SDK_ANDROID/extras/google/gcm/gcm-client/dist» donde deberá aparecer la librería «gcm.jar» que debemos añadir a nuestro proyecto.
Lo siguiente será configurar nuestro AndroidManifest. Lo primero que añadiremos será una cláusula <uses-sdk> para indicar como versión mínima del SDK soportada la 8 (Android 2.2). Con esto nos aseguraremos de que la aplicación no se instala en dispositivos con versión de Android anterior, no soportadas por el servicio GCM.
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16" />
A continuación añadiremos los permisos necesarios para ejecutar la aplicación y utilizar GCM:
<permission android:name="net.sgoliver.android.permission.C2D_MESSAGE" android:protectionLevel="signature" /> <uses-permission android:name="net.sgoliver.android.permission.C2D_MESSAGE" /> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
Los dos primeros aseguran que sólo esta aplicación podrá recibir los mensajes, el segundo permite la recepción en sí de mensajes desde GCM (sustituir mi paquete java «net.sgoliver.android» por el vuestro propio en estas lineas), el tercero es el permiso para poder conectarnos a internet y el último es necesario para tareas realizadas durante la recepción de mensajes que veremos más adelante.
Por último, como componentes de la aplicación, además de la actividad principal ya añadida por defecto, deberemos declarar un broadcast receiver llamado GCMBroadcastReceiver, que no tendremos que crearlo porque ya viene implementado dentro de la librería «gcm.jar» (solo tenéis que modificar el elemento <category> para indicar vuestro paquete java), y un servicio llamado GCMIntentService (Atención, es obligatorio este nombre exacto para el servicio si no queremos tener que implementar nosotros mismos el broadcast receiver anterior). Ya veremos más adelante para qué son estos dos componentes.
<application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > ... <receiver android:name="com.google.android.gcm.GCMBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" > <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <action android:name="com.google.android.c2dm.intent.REGISTRATION" /> <category android:name="net.sgoliver.android" /> </intent-filter> </receiver> <service android:name=".GCMIntentService" /> </application>
Una vez definido nuestro AndroidManifest con todos los elementos necesarios vamos a empezar a implementar la funcionalidad de nuestra aplicación de ejemplo.
Empezamos por la más sencilla, el botón de guardar el nombre de usuario. Como comentamos anteriormente, vamos a utilizar preferencias compartidas para esta tarea. Como éste es un tema ya visto en el curso no me detendré en el código ya que es bastante directo.
btnGuardarUsuario.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { SharedPreferences prefs = getSharedPreferences("MisPreferencias", Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putString("usuario", txtUsuario.getText().toString()); editor.commit(); } });
Como podéis comprobar nos limitamos a almacenar una nueva propiedad llamada «usuario» con el texto introducido en el cuadro de texto de la interfaz.
El siguiente botón es el de registro del cliente en GCM, y aquí sí nos detendremos un poco para comentar primero cómo funciona internamente este procedimiento.
La aplicación android debe solicitar el registro en el servicio GCM mediante una petición HTTP POST similar a la que ya vimos en el artículo anterior para la aplicación web. Por suerte, este procedimiento se ve simplificado enormemente con el uso de la librería gcm.jar, ya que el montaje y ejecución de esta petición queda encapsulado como una simple llamada a un método estático de la clase GCMRegistrar, definida en la librería. Por su parte, tanto la respuesta a esta petición de registro como la posterior recepción de mensajes se reciben en la aplicación Android en forma de intents. Y aquí es donde entran en juego los dos componentes que hemos definido anteriormente en nuestro AndroidManifest. El receiver GCMBroadcastReceiver será el encargado de «esperar y capturar» estos intents cuando se reciban y posteriormente lanzar el servicio GCMIntentService donde se procesarán en un hilo independiente estas respuestas según el intent recibido. Como ya dijimos, el broadcast receiver no será necesario crearlo ya que utilizaremos el ya proporcionado por la librería. En cambio la implementación del IntentService sí será de nuestra responsabilidad. Aunque una vez más la librería de GCM nos facilitará esta tarea como ya veremos más adelante.
Veamos primero cómo realizar el registro de nuestra aplicación en GCM al pulsar el botón de registro. Como hemos dicho esto se limitará a llamar a un método estático, llamado register(), de la clase GCMRegistrar. La única precaución que tomaremos es verificar previamente si estamos ya registrados, algo que podremos hacer fácilmente llamando al método getRegistrationId() de la misma clase. El método register() recibirá dos parámetros, el primero de ellos una referencia al contexto de la aplicación (normalmente la actividad desde la que se llama) y en segundo lugar el «Sender ID» que obtuvimos cuando creamos el nuevo proyecto en la Google API Console.
btnRegistrar.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //Si no estamos registrados --> Nos registramos en GCM final String regId = GCMRegistrar.getRegistrationId(GcmActivity.this); if (regId.equals("")) { GCMRegistrar.register(GcmActivity.this, "224338875065"); //Sender ID } else { Log.v("GCMTest", "Ya registrado"); } } });
Así de sencillo y rápido. Pero esto es sólo la petición de registro, ahora nos tocará esperar la respuesta, algo que veremos en breve.
Por su parte, el botón de «des-registro» se implementará de forma análoga, con la única diferencia que esta vez utilizaremos el método unregister() de la clase GCMRegistrar.
btnDesRegistrar.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //Si estamos registrados --> Nos des-registramos en GCM final String regId = GCMRegistrar.getRegistrationId(GcmActivity.this); if (!regId.equals("")) { GCMRegistrar.unregister(GcmActivity.this); } else { Log.v("GCMTest", "Ya des-registrado"); } } });
Ahora toca procesar las respuestas. Como hemos dicho, para hacer esto tendremos que implementar el servicio GCMIntentService. Pero no lo haremos desde cero, ya que la librería de GCM nos proporciona una clase base GCMBaseIntentService de la cual podemos extender la nuestra, con la ventaja de que tan sólo tendremos que sobrescribir unos pocos métodos a modo de callbacks, uno por cada posible respuesta o mensaje que podemos recibir desde el servicio GCM. Estos métodos son:
- onRegistered(context, regId). Se llamará al recibirse una respuesta correcta a la petición de registro e incluye como parámetro el Registration ID asignado a nuestro cliente.
- onUnregistered(context, regId). Análogo al anterior pero aplicado a una petición de «des-registro».
- onError(context, errorId). Se llamará al recibirse una respuesta de error a una petición de registro o des-registro. El código de error concreto se recibe como parámetro.
- onMessage(context, intent). Se llamará cada vez que se reciba un nuevo mensaje desde el servidor de GCM. El contenido del mensaje se recibe en forma de intent, el cual veremos más adelante cómo procesar.
Empecemos por el método onRegistered(). Al recibir una respuesta satisfactoria a la petición de registro recuperaremos el nombre de usuario almacenado y junto con el Registration ID recibido nos conectaremos al servicio web que creamos en el artículo pasado pasándole dichos datos. Esto completará el registro tanto con el servidor de GCM como con nuestra aplicación web.
protected void onRegistered(Context context, String regId) { Log.d("GCMTest", "REGISTRATION: Registrado OK."); SharedPreferences prefs = context.getSharedPreferences("MisPreferencias", Context.MODE_PRIVATE); String usuario = prefs.getString("usuario", "por_defecto"); registroServidor(usuario, regId); }
El método registroServidor() será el encargado de realizar la conexión al servicio web y de la llamada al método web de registro. No me detendré en comentar el código de este método porque es análogo a los ejemplos ya vistos en el artículo que dedicamos a servicios web SOAP en Android. Veamos tan sólo el código:
private void registroServidor(String usuario, String regId) { final String NAMESPACE = "http://sgoliver.net/"; final String URL="http://10.0.2.2:1634/ServicioRegistroGCM.asmx"; final String METHOD_NAME = "RegistroCliente"; final String SOAP_ACTION = "http://sgoliver.net/RegistroCliente"; SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME); request.addProperty("usuario", usuario); request.addProperty("regGCM", regId); SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); envelope.dotNet = true; envelope.setOutputSoapObject(request); HttpTransportSE transporte = new HttpTransportSE(URL); try { transporte.call(SOAP_ACTION, envelope); SoapPrimitive resultado_xml =(SoapPrimitive)envelope.getResponse(); String res = resultado_xml.toString(); if(res.equals("1")) Log.d("GCMTest", "Registro WS: OK."); } catch (Exception e) { Log.d("GCMTest", "Registro WS: NOK. " + e.getCause() + " || " + e.getMessage()); } }
Por su parte, en los métodos de des-registro y de error me limitaré a escribir un mensaje en el log de la aplicación para no complicar demasiado el ejemplo, pero en una aplicación real deberíamos verificar estas respuestas.
@Override protected void onUnregistered(Context context, String regId) { Log.d("GCMTest", "REGISTRATION: Desregistrado OK."); } @Override protected void onError(Context context, String errorId) { Log.d("GCMTest", "REGISTRATION: Error -> " + errorId); }
Por último, en el método onMessage() procesaremos el intent con los datos recibidos en el mensaje y mostraremos una notificación en la barra de estado de Android. El intent recibido contendrá un elemento en su colección de extras por cada dato adicional que se haya incluido en la petición que hizo la aplicación servidor al enviar el mensaje. ¿Recordáis? Aquellos datos adicionales que había que preceder con el prefijo «data.». Si hacéis memoria, en nuestros mensajes de ejemplo tan sólo incluíamos un dato llamado «data.msg» con un mensaje de prueba. Pues bien, estos datos se recuperarán de la colección de extras del intent llamado al método getString() con el nombre del dato, pero esta vez eliminando el prefijo «data.». Veamos cómo quedaría todo esto:
@Override protected void onMessage(Context context, Intent intent) { String msg = intent.getExtras().getString("msg"); Log.d("GCMTest", "Mensaje: " + msg); mostrarNotificacion(context, msg); }
Simple, ¿no?. Al final del método llamamos a un método auxiliar mostrarNotificacion() que será el encargado de mostrar la notificación en la barra de estado de Android. Esto también vimos como hacerlo en detalle en un artículo anterior por lo que tampoco comentaremos el código:
private void mostrarNotificacion(Context context, String msg) { //Obtenemos una referencia al servicio de notificaciones String ns = Context.NOTIFICATION_SERVICE; NotificationManager notManager = (NotificationManager) context.getSystemService(ns); //Configuramos la notificación int icono = android.R.drawable.stat_sys_warning; CharSequence textoEstado = "Alerta!"; long hora = System.currentTimeMillis(); Notification notif = new Notification(icono, textoEstado, hora); //Configuramos el Intent Context contexto = context.getApplicationContext(); CharSequence titulo = "Nuevo Mensaje"; CharSequence descripcion = msg; Intent notIntent = new Intent(contexto, GCMIntentService.class); PendingIntent contIntent = PendingIntent.getActivity( contexto, 0, notIntent, 0); notif.setLatestEventInfo( contexto, titulo, descripcion, contIntent); //AutoCancel: cuando se pulsa la notificaión ésta desaparece notif.flags |= Notification.FLAG_AUTO_CANCEL; //Enviar notificación notManager.notify(1, notif); }
Y sólo una indicación más, además de sobrescribir estos métodos en nuestra clase GCMIntentService, también tendremos que añadir un nuevo constructor sin parámetros que llame directamente al constructor de la clase base pasándole de nuevo el Sender ID que obtuvimos al crear el nuevo proyecto en la Google API Console. Quedaría algo así:
public GCMIntentService() { super("224338875065"); }
Si no ha quedado claro del todo cómo quedaría la clase GCMIntentService completa puede descargarse y consultarse el código fuente completo al final del artículo.
Y con esto habríamos terminado de implementar nuestra aplicación Android capaz de recibir mensajes push desde nuestra aplicación web de ejemplo. Si ejecutamos ambas y todo ha ido bien, introducimos un nombre de usuario en la aplicación Android, pulsamos «Aceptar» para guardarlo, nos registramos como clientes en GCM pulsando el botón «Registrar GCM», y seguidamente desde la aplicación web introducimos el mismo nombre de usuario del cliente, pulsamos el botón «Enviar GCM» y en breves segundos nos debería aparecer la notificación en la barra de estado de nuestro emulador como se observa en las imágenes siguientes:
Y si desplegamos la barra de estado veremos el mensaje de prueba recibido:
Como habéis podido comprobar en estos tres últimos artículos, la utilización de mensajes push requiere de un proceso algo laborioso pero para nada complicado. Os animo a que lo intentéis en vuestras aplicaciones ya que puede representar una característica interesante y útil.
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.
17 comentarios
Genial! estuve probando un poco el código (desde otra fuente) pero con una aplicación hecha en java para enviar los datos al terminal androide.
Saludos
Esto es genial, pero no he podido ponerlo a funcionar desde un dispositivo, desde el el emulador todo es ok, pero en el log del celular siempre me sale «AUTHENTICATION_FAILED», ya he verificado y la cuenta de gmail esta sincronizada pero siempre que trata de registrarse me saca ese error, sera que me podrias dar una mano?
Hola!
Sólo me queda una duda, cuál es la longitud exacta o máxima que puede tener el Registration ID asignado al cliente?
Bueno primero que todo felicitaciones por la guia, me a sido de bastante ayuda, cuesta mucho encontrar ayuda como esta.
estaba probando el ejemplo que desarrollaste sobre este tema (GCM) y al ejecutar el emulador me lanza un error y se cierra la aplicacion, creo que ocurre por las lineas de codigo que checkean
GCMRegistrar.checkDevice(MainActivity.this);
GCMRegistrar.checkManifest(MainActivity.this);
Estoy usando android 2.3.3, ojala me pudiesen ayudar, saludos
Buenas, muy buen articulo, enhorabuena, pero consigo un error que me trae de cabeza. Para utilizar gcm en el emulador tengo entendido que hace falta una cuenta configurada en él. Pues bien a la hora de configurarla me da el siguiente error: ‘Couldn´t open connection to server’. Tengo configurado un proxy en el emulador y tengo acceso a internet a través del browser, esto es lo raro, ¿alguien podría darme una pista de que me pasa?
Utilizo Android 4.2, el emulador, así como el proyecto tienen targets Google API.
Saludos y muchas gracias
Buen dia.
mi pregunta es la siguiente cuando se le da al boton de registrar.
automaticamente el debe r al metodo onregister de la clase GCMIntentService??
muy bueno el tutorial!!! lastima que el api de google sea un tanto esquivo a la hora de funcionar :)
seria interesante que dieras (si fuese posible) ejemplos para usar pero con PHP del lado del servidor, saludos!!!
¿Y el permiso GET_ACCOUNTS es necesario o no? En la documentación oficial de GCM lo ponen, pero lo cierto es que muchas apps con GCM no lo tienen y aparentemente no pasa nada…
Esto va para Geonick (y para quien no lo sepa); si usan el emulador y les da error, asegurense de que el Target Name diga: «Google APIs (Google Inc.)» y no Android x.x.x, esto se puede cambiar desde el android virtual device manager y no es necesario crear un nuevo emulador virtual con solo cambiar el target alcanza, ademas en mis pruebas de momento solo me funciono en host remoto, con localhost todavia no
Hola, muchas gracias por los tutoriales estan espectaculares!!!!! Tengo una duda, si el servicio gcm renueva el Registration ID como lo informa?
¡Hola Salvador! Tus tutoriales son siempre de gran ayuda y de fácil comprensión.
No se si debe faltar algo en la parte dedicada a des-registrar el dispositivo, ya que dices que habría que hacerlo más elaborado. El caso es que, una vez des-registrado y comprobado que el evento onUnregistered es lanzado, sigo recibiendo las notificaciones enviadas del servidor.
Un saludo.
Muy buen curso el que te estas currando. Este apartado del curso me ha venido genial, pero se echa en falta que hablaras sobre como tratar las notificaciones recibidas ( es decir al pinchar encima de ellas que lance X activity )
Me queda una duda… en que momento se especificó que las notificaciones deben lanzarse así la aplicación no este en ejecución, es decir: el hecho de que se presione el botón registrar GCM hace que se genere una petición al servidor, pero si la aplicación no se ejecuta, ¿Como hace para que se genere la notificación?
Buenas, tengo una app y me gustaría una vez enviada la notificación push al otro usuario como desactivar el servicio, es decir, yo tengo una activity y hago uso de la clase GCMIntentService para mandar la notificación, a continuación quiero desactivar esta clase porque más adelante vuelvo a hacer uso del sistema push pero en otro activity y utilizando otra clase GCMIntentService2. Ya que el problema es que al recibir el push en el otro dispositivo lo recibe por el GCMIntentService y no por el nuevo GCMIntentService2.
Muchas Gracias por tu tiempo y muy buen tuto!!
Tengo una duda, las cosas echas con el ejemplo anterior ya no funcionaran?
ATENCION:
La API acaba de cambiar de revisión y la «Librería Google Cloud Messaging for Android» que se indica en la captura ha sido declarada deprecated y ahora se recomienda usar GoogleCloudMessaging, por lo que el ejemplo de cliente cambia en la forma de registro y de envío (entre otras).
Me ha pillado el cambio justo en mitad de la prueba… y estoy haciendo el cambio a la nueva versión. También he cambiado el lado del servidor por PHP+MySQL. Cuando tenga algo funcional se lo paso a sgoliver. Saludos!! Y gracias por el tutorial !!!!!
Más info aquí:
http://developer.android.com/intl/es/reference/com/google/android/gcm/package-summary.html
PD: Si incluyes gcm.jar (ahora obsoleta) debería seguir funcionando… pero he preferido dar el salto definitivo…
[…] Implementación del Cliente Android [v3] [Ver nueva versión] […]