«

»

jun 01 2012

C2DM – Notificaciones Push (Parte II)

En un post anterior hicimos una introducción a las Notificaciones Push y como enviar estas desde servidor. Hoy, como os prometimos, nos centraremos en preparar nuestras apps para recibirlas y tratarlas como mas nos convenga.

 


Una vez hemos dado de alta nuestra app en los servidores de C2DM de Google, lo primero será añadir en el Manifest los permisos necesarios para poder trabajar:

 

Permiso para el registro y recepción de mensajes

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

Permiso de acceso a Internet

<uses-permission android:name="android.permission.INTERNET"/>

Solo nuestra app podrá recibir los mensajes y respuestas del registro

<permission android:name="com.example.myapp.permission.C2D_MESSAGE" android:protectionLevel="signature" />

<uses-permission android:name="com.example.myapp.permission.C2D_MESSAGE" />

 

Proceso de registro

 

Para que nuestra app pueda recibir notificaciones en nuestro terminal deberemos cada instancia de la misma, es decir, cada app en cada terminal, deberá darse de alta para que lo servidores sepan de su existencia. En el siguiente código os mostramos como realizar esta petición de registro para obtener nuestro ID necesario para recibir estas notificaciones.

Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
registrationIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0));
registrationIntent.putExtra("sender", "mymailsender@gmail.com");
        
startService(registrationIntent);

 

Como podéis comprobar en la petición indicamos un mail de Sender que es el que previamente hemos indicado al dar de alta la app en los servidores de C2DM. Esta petición la podemos realizar en cualquier momento, lo normal es hacerlo en el onCreate de nuestra primera activity para nada mas arrancar nuestra app este lista para recibir mensajes. Pero como es evidente aquí no acaba la cosa, solo hemos solicitado el registro, pero el mismo ni se ha terminado de realizar ni hemos recibido aun el ID necesario.

 

Para terminar el proceso de registro deberemos gestionar la respuesta del servidor a nuestra petición, y para ello implementaremos un BroadcastReceiver, el cual deberemos dar de alta en el Manifest:

<receiver android:name=".C2DMReceiver" android:permission="com.google.android.c2dm.permission.SEND" >

    <!-- Receive the actual message -->
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />

        <category android:name="com.example.myapp" />
    </intent-filter>
    <!-- Receive the registration id -->
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

        <category android:name="com.example.myapp" />
    </intent-filter>
</receiver>

 

Como podéis comprobar configuramos dos intent-filters, uno para recibir las respuestas de las peticiones de registro y otro para recibir los mensajes o notificaciones. Aunque este apartado solo explicaremos lo primero, así ya lo tendremos configurado para la segunda parte del tutorial.

 

A continuación os exponemos el código de nuestro BroadcastReceiver preparado para tratar la respuesta de la petición de registro y las posibles respuestas de error del mismo. Aunque no lo tratamos aquí (nos limitamos a mostrar el resgistrationID por consola), el ID recibido debería ser enviado a nuestros servidores y almacenado para que los mismos puedan mandarnos notificaciones a partir del mismo. Para hacer las pruebas bastará con ojear la consola y copiar el ID recibido que usaremos con los comandos comentados en el anterior post.

public class C2DMReceiver extends BroadcastReceiver {

	private static final String TAG = "C2DM_RECEIVER";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		if (intent.getAction().equals(
				"com.google.android.c2dm.intent.REGISTRATION")) {

			String registrationId = intent.getStringExtra("registration_id");
			String error = intent.getStringExtra("error");
			String unregistered = intent.getStringExtra("unregistered");

			if (error != null) {
				// Registration failed.
				if (error.equals("SERVICE_NOT_AVAILABLE")) {
					Log.e(TAG, "Service not available.");
					// Retry using exponential back off.
				} else if (error.equals("ACCOUNT_MISSING")) {
					Log.e(TAG, "No Google account on device.");
					// Ask the user to create / add a Google account
				} else if (error.equals("AUTHENTICATION_FAILED")) {
					Log.e(TAG, "Incorrect password.");
					// Ask the user to re-enter their Google account password.
				} else if (error.equals("TOO_MANY_REGISTRATIONS")) {
					Log.e(TAG, "Too many applications registered.");
					// Ask the user to unregister / uninstall some applications.
				} else if (error.equals("INVALID_SENDER")) {
					Log.e(TAG, "Invalid sender account.");
					// The sender account specified has not been registered
					// with the C2DM server.
				} else if (error.equals("PHONE_REGISTRATION_ERROR")) {
					Log.e(TAG, "Phone registration failed.");
					// The phone doesn't currently support C2DM.
				}
			} else if (unregistered != null) {
				// Unregistration complete. The application should stop
				// processing any further received messages.
				Log.d(TAG, "Phone deregistration completed successfully.");
			} else if (registrationId != null) {
				Log.d(TAG, "C2DM egistration ID received = " + registrationId);
				// Send the registration ID to your server.
			}
		}

	}

}

 

El modo en que actuemos en función de cada error o evento de los que se indican lo dejo a vuestra elección, pero os muestro todas las posibles respuestas.

 

Recepción de mensajes

 

Para la recepción de mensajes bastará con añadir una líneas más a nuestro BroadcastReceiver para tratar la entrada de los mismos:

if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
    Bundle extras = intent.getExtras();

    // Extract any extras included in the server messages.
    String message = extras.getString("dato1");
    String message = message + extras.getString("dato2");
    Log.d(TAG, "Mensaje recibido = " + message);
}

 

En extras.getString(“dato”), en “dato” indicaríamos cada uno de los datos que hemos incluido al enviar la notificación, es decir, cada uno de los datos que hemos etiquetado como data.dato1, data.dato2…

En este caso nos hemos limitado a mostrar los datos recibidos en consola pero al igual que con el tratamiento de registro las posibilidades son múltiples, desde generar una notificación hasta activar un proceso.

 

Como podéis comprobar no tiene mayor misterio y la dificultad no es elevada, y con esto ya tendríamos nuestra app recibiendo notificaciones. Espero que os sea de utilidad.

 

Fuente | Ingens Blog

Acerca del autor

JMPergar

Mobile Developer at @BeRepublic & Founder of @AndroCode. Silver Speaker & Member of Core Team at @GDGBarcelona.

  • kentaki

    Hola!
    Muy bueno el tutorial, ya lo tengo funcionando. El caso es que tengo un problema que llevo ya días con él y no hay manera.
    Necesito crear una notificación al recibir el mensaje de c2dm. Cuando el móvil está bloqueado, en reposo, no se crea!! ¿Sabes por qué puede suceder? ¿Tú como lo harías para crear una notificación en la misma clase del Brodcast Receiver?

    Gracias y un saludo!

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      ¿Y cuando activas el terminal llega se crea la notificacion o si esta inactivo no se crea nunca? ¿Entiendo que si tienes el movil activo si se crea? Asegurate que el valor de “delay_while_idle” es “false”, en teoria por defecto es “false” asi que si no lo indicas debería de funcionar., pero prueba si las respuestas a mis preguntas es “si” yo probaria con el “delay_while_idle”. Ya me cuentas…

      • kentaki

        He conseguido que funcione con el móvil bloqueado, gracias.
        Ahora el problema es que sólo funciona si salgo de la aplicación con el botón HOME. Si salgo con el botón ATRÁS, la notificación ya no aparece. Aparece sólo al abrir la aplicación.
        He intentado arreglarlo con WakeLock pero parece que no tiene nada que ver.

        Un saludo y muchas gracias!

        • Antonio A

          Hola, tengo este problema:

          Mientras la aplicación está en ejecución, e incluso en modo background, funciona perfectamente, recibo correctamente la notificación.

          El problema es que cuando la tengo cerrada por completo, al abrirla no consigo que me lleguen las notificaciones.

          Agradecería vuestra ayuda.

          • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

            Hace mucho que toque esta parte y no me he vuelto a poner con ello, pero si no recuerdo mal en algún punto se configuraba que la notificación despertara o no la app. No obstante a partir del Google I/O publicaron una nueva versión de la API que facilitaba la implementación de las notificaciones push. En el curso de sgoliver podrás encontrar un tutorial del nuevo método de implementación:

            http://www.sgoliver.net/blog/?page_id=3011

  • Juan

    Hola, genia el ejemplo!!
    Lo unico que me queda una duda, para enviar las notificaciones desde donde las tengo que enviar ???
    Tengo que tener un servidor o hay alguna manera desde consola incluso para poder mandarlas ?

    Saludos y Gracias

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      En la primera parte del tutorial se explica como hacerlo: http://androcode.es/2012/05/c2dm-notificaciones-push-parte-i/

      • Juan

        Muchas gracias!!

        Estado investigando y haciendo tal como me dices el tutorial de la parte 1, pero resulta que no llega la respuesta ni por log ni nada de nada, las notificaciones pueden tardar mucho en llegar ??

        Me sale esto cuando intento mandar el mensaje por comando comando

        id=0:1339688116884099%be026d1300000030Mac-Pro-de-Juan:~ juan$

        Muchas gracias!!

        • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

          Esto te sale en la consola? o en log de tu app? Creo que lo indique pero por si acaso te lo comento, esos comandos son para Linux

          • Juan

            Si, en efecto lo comentaste lo del linux, de echo lo realizo todo desde un Mac con lo cual tiene linux, alguna idea ?

            Gracias!!

  • Rul

    Muy bueno el tutorial gracias por tu tiempo que seguro excasea con tanto curro y tutoriales.

    Yo ya hice una aplicación que me genera notificaciones y me las envía a mi móvil para avisarme si unas tareas quartz se ejecutan correctamente o no, vamos digamos logs en lugar de por correo al móvil. Y mi pregunta es lo intente siguiendo el patrón de diseño de notificaciones para ICS, que este te dice que cuando recibas varias notificaciones del mismo tipo se acumulen y te salga un contador en lugar de 4 notificaciones, el caso es que no he sido capaz de hacer de ninguna manera de enviar dos notificaciones y que me queden agrupadas, no se si me explico sorry.

    Pero si has sido capaz de entenderme a ver si tu has sido capaz de consegirlo, o a lo mejor no se puede por temas de ids de notificación o algo por el estilo.

    Un saludo y gracias.

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      A ver que te puedo contestar con lo que he entendido ;) jeje

      No he leido el patron que comentas así que te cuento lo que veo… Creo que es mas de una cuestion de la creacion de la notificacion en el terminal y no del envio del mensaje (en esto no he entrado en este tutorial). Cuando enviamos una notificacion gracias al parametro collapse_key podemos hacer que si se acumulan varias notificaciones iguales en servidor se acumulen y solo nos llegue una, pero en tu caso creo que deberian de llegar todas. Yo lo que haria al recibir una notificacion comprobar si hay ya otra activa (la verdad que ahora mismo no se si esto se puede hacer pero supongo que si) y si la hay destruirla y crear una nueva teniendo en cuenta esto. Me da la impresion que es un poco asi como funciona.

      Espero que te sirva de algo ;)

      PD: Si me pasa el enlace de ese patron que comentas le echo un ojo

  • Carlos

    Muchas gracias y enhorabuena por el trabajo realizado en el post.

    En realidad lo mío es una pregunta genérica, que cualquiera puede contestar y se trata de los siguiente: Obtengo “token” del terminal, lo almaceno en mi base de datos, consigo el Auth Code para mi correo en el servidor c2dm desde la consola MAC con el comando…

    1
    curl https://www.google.com/accounts/ClientLogin -d Email=xxx -d "Passwd=xxx" -d accountType=GOOGLE -d source=Google-cURL-Example -d service=ac2dm

    … pero cuando ejecuto el comando para enviar al servidor un mensaje me dice que no autorizado.

    1
    curl --header "Authorization: GoogleLogin auth=XXX" "https://android.apis.google.com/c2dm/send" -d registration_id=XXX -d "data.payload=payload" -d collapse_key=0

    ¿Cómo es posible que no dé error al recoger el Auth Code con la primera sentencia y que después me diga que no es correcto? La respuesta que devuelve es la siguiente:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <HTML>
    <HEAD>
    <TITLE>Unauthorized</TITLE>
    </HEAD>
    <BODY BGCOLOR="#FFFFFF" TEXT="#000000">
    <H1>Unauthorized</H1>
    <H2>Error 401</H2>
    </BODY>
    </HTML>
    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      Has comprado si has introducido bien los ID’s?

  • Sebastian

    Muy buen tutorial. Soy nuevo en esto de android y aunque esta explicado de manera muy clar sigo teniendo dudas. Podrias subir el proyecto entero para poder visualizarlo de mejor manera?
    Gracias de Antemano.
    Saludos desdes Ecuador.

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      Pregunta las dudas y te contesto así aprendemos todos ;)

  • http://vsion.me Ivan

    Hola.

    El tutorial esta genial. Mi enhorabuena. Quería comentaros una cosa y a ver si vosotros sabéis algo del tema.
    Después de leer el tutorial me he ido a la página de C2DM y me he dado de alta. Me llaga un correo diciendo que próximamente me dan de alta. Hasta ahí bien. Rato después vuelvo a acceder a esta página y me sale un mensaje diciendo que C2DM esta deprecado desde el 26-06-2012. Osea ayer y lo raro es que un rato antes no me dijo nada. En el mismo mensaje informan de que en sustitución de C2DM estará GCM (Google Cloud Messaging ) Mirando un poco, porque de esto no hay mucha info, parece que esto entrara con Jelly Bean y que es una evolución de C2DM. ¿Sabéis vosotros algo de esto? ¿Habrá compatibilidad?

    Muchas gracias y gran trabajo el que hacéis aquí.

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      Esto acaba de se presentado en el Google I/O de este año y aún te puedo decir poco, pero pronto tendremos un tutorial en AndroCode, uno de nuestros redactores esta en ello.

  • mªcarmen

    desde hace tiempo no me llegan mis menciones de twitter al movil vamos el pajarito no sale arriba ? y antes si me gustaria saber porque noo salen si tengo todo sincronizado en mis cuentass , un saludo besitos

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      Este no es el sitio mas adecuado para hacer este tipo de preguntas ;) y ademas no sabría decirte, puede que la app tenga un bug o el movil, probaría a desinstalar la app e instalarla otra vez, y en el peor de los casos wipe del terminal. Esto suponiendo que has revisado bien las configuraciones de la app.

  • phipex

    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?

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      Hace poco cambiarón la forma de implementar esta funcionalidad y puede que exista algún problema generado por este cambio. Aún no hemos públicado el post al respecto (pronto lo haremos). Mientras tanto te recomiendo pasarte por http://www.sgoliver.net/blog/?page_id=3011

  • http://guitarworld7.com alexander

    como puedo mostrar el contenido de la notificacion push en un label o formulario

  • dmrsoluciones

    Hola Gente, como andan?

    Tengo el problema que la aplicacion android dice”Lo sentimos . La aplicacion xxxx se detuvo” para trabajar con los push notificaction, cuando realice el debugger eso sucede cuando pasa por:

    InstanceID instanceID = InstanceID.getInstance(context);

    token = instanceID.getToken(key_id.toString(),

    GoogleCloudMessaging.INSTANCE_ID_SCOPE); error en el getToken,

    note que el token lo genera pero se cierra la aplicación , error:

    The method ‘java.io.File android.support.v4.content.ContextCompat.getNoBackupFilesDir(android.content.Context)’ was expected to be of type virtual but instead was found to be of type direct (declaration of ‘java.lang.reflect.ArtMethod’ appears in /system/framework/core-libart.jar)

    estoy utilizando android studio, para

    cdvCompileSdkVersion=android-22

    cdvBuildToolsVersion=23.0.3

    Saben cual puede ser el problema.

    Saludos,

    Diego