Inicio Android Notificaciones Push Android: Google Cloud Messaging (GCM). Implementación Cliente (Nueva Versión)

Notificaciones Push Android: Google Cloud Messaging (GCM). Implementación Cliente (Nueva Versión)

por sgoliver

En los apartados anteriores del curso hemos hablado sobre el servicio Google Cloud Messaging y hemos visto cómo 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 apartado nos centraremos en la aplicación Android cliente.

Esta aplicación cliente, como ya hemos comentado en alguna ocasión será responsable de:

  1. Registrarse contra los servidores de GCM como cliente capaz de recibir mensajes.
  2. Almacenar el «Registration ID» recibido como resultado del registro anterior.
  3. Comunicar a la aplicación web el «Registration ID» de forma que ésta pueda enviarle mensajes.
  4. Recibir y procesar los mensajes desde el servidor de GCM.

En la versión anterior de GCM, las tareas 1 y 4 se realizaban normalmente utilizando como ayuda una librería adicional (gcm.jar) proporcionada por Google. Sin embargo, en la nueva versión de GCM incluida como parte de los Google Play Services cambian un poco la filosofía de trabajo y esta librería ya no es necesaria.

Por su parte, 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 apartado anterior, sirviéndonos para ello de la librería ksoap2, tal como ya describimos en el capítulo sobre servicios web SOAP en Android.

Durante el capítulo construiremos una aplicación de ejemplo muy sencilla, en la que el usuario podrá introducir un nombre de usuario identificativo y pulsar un botón para que quede guardado en las preferencias de la aplicación. Tras esto podrá registrarse como cliente capaz de recibir mensajes desde GCM pulsando un botón llamado «Registrar». 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 servidor a través del servicio web. Obviamente todo este proceso de registro debería hacerse de forma transparente para el usuario de una aplicación real, en esta ocasión he colocado un botón para ello sólo por motivos didácticos y para poder hacer una prueba más controlada.

demo gmc

Como en el caso de cualquier otro servicio incluido en los Google Play Services el primer paso para crear nuestra aplicación Android será importar el proyecto de librería de los servicios, crear nuestro propio proyecto y finalmente hacer referencia a la librería desde nuestro proyecto. Todo este proceso está explicado en el artículo de introducción a los Google Play Services.

El siguiente paso será configurar nuestro AndroidManifest. Lo primero que revisaremos será la cláusula <usessdk>, donde como versión mínima del SDK debemos indicar la 8 (Android 2.2) o superior. Con esto nos aseguraremos de que la aplicación no se instala en dispositivos con versión de Android anterior, no soportadas por los Google Play Services.

A continuación añadiremos los permisos necesarios para ejecutar la aplicación y utilizar GCM:

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

<permission android:name="net.sgoliver.android.newgcm.permission.C2D_MESSAGE"
     android:protectionLevel="signature" />
<uses-permission android:name="net.sgoliver.android.newgcm.permission.C2D_MESSAGE" />

El primero (INTERNET) nos dará acceso a internet en la aplicación, el segundo (GET_ACCOUNTS) es necesario porque GCM requiere una cuenta de Google configurada en el dispositivo, el tercero (WAKE_LOCK) será necesario para utilizar un determinado tipo de broadcast receiver que comentaremos más adelante, el cuarto (RECEIVE) es el que permitirá que la aplicación se registre y reciba mensajes de GCM. Los dos últimos aseguran que sólo nosotros podremos recibir los mensajes de nuestra aplicación (sustituir mi paquete java «net.sgoliver.android.newgcm» por el vuestro propio en estas dos lineas).

Por último, como componentes de la aplicación, además de la actividad principal ya añadida por defecto, deberemos declarar un broadcast receiver, que llamaremos GCMBroadcastReceiver (tenéis que modificar el elemento <category> con vuestro paquete java), y un servicio que llamaremos GCMIntentService. Más adelante veremos cuál será el cometido de cada uno de estos componentes.

<application
     android:allowBackup="true"
     android:icon="@drawable/ic_launcher"
     android:label="@string/app_name"
     android:theme="@style/AppTheme" >

     ...

     <receiver
         android:name=".GCMBroadcastReceiver"
         android:permission="com.google.android.c2dm.permission.SEND" >
         <intent-filter>
              <action android:name="com.google.android.c2dm.intent.RECEIVE" />
              <category android:name="net.sgoliver.android.newgcm" />
         </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. Empezaremos por el proceso de registro que se desencadena al pulsar el botón «Registrar» de la aplicación tras introducir un nombre de usuario.

Nuestro botón de registro tendrá que realizar las siguientes acciones:

  1. Verificar que el dispositivo tiene instalado Google Play Services.
  2. Revisar si ya tenemos almacenado el código de registro de GCM (registration id) de una ejecución anterior.
  3. Si no disponemos ya del código de registro realizamos un nuevo registro de la aplicación y guardamos los datos.

El código del botón con estos tres pasos, que iremos comentando por partes, sería el siguiente:

btnRegistrar.setOnClickListener(new OnClickListener() {

	@Override
	public void onClick(View v)
	{
		context = getApplicationContext();

		//Chequemos si está instalado Google Play Services
		//if(checkPlayServices())
		//{
		        gcm = GoogleCloudMessaging.getInstance(MainActivity.this);

		        //Obtenemos el Registration ID guardado
		        regid = getRegistrationId(context);

		        //Si no disponemos de Registration ID comenzamos el registro
		        if (regid.equals("")) {
		    		TareaRegistroGCM tarea = new TareaRegistroGCM();
		    		tarea.execute(txtUsuario.getText().toString());
		        }
		//}
		//else
		//{
	        //    Log.i(TAG, "No se ha encontrado Google Play Services.");
	        //}
	}
});

El chequeo de si están instalados los Google Play Services en el dispositivo no se comporta demasiado bien al ejecutar la aplicación sobre el emulador (dependiendo de la versión de Android utilizada) por lo que he decidido mantenerlo comentado para este ejemplo, pero en una aplicación real sí debería realizarse. Además, también debería incluirse en el evento onResume() de la actividad:

@Override
protected void onResume()
{
    super.onResume();

//    checkPlayServices();
}

En cuanto a la lógica para hacer el chequeo podremos ayudarnos de la clase GooglePlayServicesUtil, que dispone del método isGooglePlayServicesAvailable() para hacer la verificación. En caso de no estar disponibles (si el método devuelve un valor distinto a SUCCESS) aún podemos mostrar un diálogo de advertencia al usuario dando la posibilidad de instalarlos. Esto lo haremos llamando al método getErrorDialog() de la misma clase GooglePlayServicesUtil. Quedaría algo así:

private boolean checkPlayServices() {
    int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
    if (resultCode != ConnectionResult.SUCCESS)
    {
        if (GooglePlayServicesUtil.isUserRecoverableError(resultCode))
        {
            GooglePlayServicesUtil.getErrorDialog(resultCode, this,
                    PLAY_SERVICES_RESOLUTION_REQUEST).show();
        }
        else
        {
            Log.i(TAG, "Dispositivo no soportado.");
            finish();
        }
        return false;
    }
    return true;
}

Si Google Play Services está instalado en el dispositivo el siguiente paso será comprobar si ya tenemos guardado los datos de registro de una ejecución anterior, en cuyo caso no habrá que volver a hacer el registro (salvo en contadas ocasiones que comentaremos ahora). Esta comprobación la heremos dentro de un método llamado getRegistrationId(), que entre otras cosas hará uso de preferencias compartidas (Shared Preferences) para recuperar los datos guardados. Nuestra aplicación guardará 4 preferencias, que definiremos en nuestra actividad como constantes:

private static final String PROPERTY_REG_ID = "registration_id";
private static final String PROPERTY_APP_VERSION = "appVersion";
private static final String PROPERTY_EXPIRATION_TIME = "onServerExpirationTimeMs";
private static final String PROPERTY_USER = "user";

La primera de ellas es el código de registro de GCM, la segunda guardará la versión de la aplicación para la que se ha obtenido dicho código, la tercera indicará la fecha de caducidad del código de registro guardado, y por último guardaremos el nombre de usuario.

En el método getRegistrationId() lo primero que haremos será recuperar la preferencia PROPERTY_REG_ID. Si ésta no está informada saldremos inmediatamente del método para proceder a un nuevo registro.

Si por el contrario ya teníamos un registration_id guardado podríamos seguir utilizándolo sin tener que registrarnos de nuevo (lo devolveremos como resultado), pero habrá tres situaciones en las que queremos volver a realizar el registro para asegurarnos de que nuestra aplicación pueda seguir recibiendo mensajes sin ningún problema:

  • Si el nombre de usuario ha cambiado.
  • Si la versión de la aplicación ha cambiado.
  • Si se ha sobrepasado la fecha de caducidad del código de registro.

Para verificar esto nuestro método recuperará cada una de las preferencias compartidas, realizará las verificaciones indicadas y en caso de cumplirse alguna de ellas saldrá del método sin devolver el antiguo registration_id para que se vuelva a realizar el registro.

private String getRegistrationId(Context context)
{
    SharedPreferences prefs = getSharedPreferences(
	MainActivity.class.getSimpleName(),
        Context.MODE_PRIVATE);

    String registrationId = prefs.getString(PROPERTY_REG_ID, "");

    if (registrationId.length() == 0)
    {
        Log.d(TAG, "Registro GCM no encontrado.");
        return "";
    }

    String registeredUser =
	prefs.getString(PROPERTY_USER, "user");

    int registeredVersion =
	prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);

    long expirationTime =
        prefs.getLong(PROPERTY_EXPIRATION_TIME, -1);

    SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.getDefault());
    String expirationDate = sdf.format(new Date(expirationTime));

    Log.d(TAG, "Registro GCM encontrado (usuario=" + registeredUser +
	", version=" + registeredVersion +
	", expira=" + expirationDate + ")");

    int currentVersion = getAppVersion(context);

    if (registeredVersion != currentVersion)
    {
        Log.d(TAG, "Nueva versión de la aplicación.");
        return "";
    }
    else if (System.currentTimeMillis() > expirationTime)
    {
    	Log.d(TAG, "Registro GCM expirado.");
        return "";
    }
    else if (!txtUsuario.getText().toString().equals(registeredUser))
    {
    	Log.d(TAG, "Nuevo nombre de usuario.");
        return "";
    }

    return registrationId;
}

private static int getAppVersion(Context context)
{
    try
    {
        PackageInfo packageInfo = context.getPackageManager()
                .getPackageInfo(context.getPackageName(), 0);

        return packageInfo.versionCode;
    }
    catch (NameNotFoundException e)
    {
        throw new RuntimeException("Error al obtener versión: " + e);
    }
}

Como podéis observar, para consultar la versión actual de la aplicación utilizamos un método auxiliar getAppVersion() que obtiene la versión mediante el Package Manager y su método getPackageInfo().

Bien, pues llegados aquí si el método anterior nos ha devuelto un código de registro (es decir, que ya teníamos uno guardado) no tendríamos que hacer nada más, significaría que ya estamos registrados en GCM y tan sólo tenemos que esperar a recibir mensajes. En caso contrario, tendremos que realizar un nuevo registro, de lo que nos ocuparemos mediante la tarea asíncrona TareaRegistroGCM.

Esta tarea asíncrona tendrá que realizar tres acciones principales: registrar la aplicación contra los servidores de GCM, registrarnos contra nuestro propio servidor al que tendrá que enviar entre otras cosas el registration_id obtenido de GCM, y por último guardar como preferencias compartidas los nuevos datos de registro.

private class TareaRegistroGCM extends AsyncTask<String,Integer,String>
{
	@Override
        protected String doInBackground(String... params)
	{
            String msg = "";

            try
            {
                if (gcm == null)
                {
                    gcm = GoogleCloudMessaging.getInstance(context);
                }

                //Nos registramos en los servidores de GCM
                regid = gcm.register(SENDER_ID);

                Log.d(TAG, "Registrado en GCM: registration_id=" + regid);

                //Nos registramos en nuestro servidor
                boolean registrado = registroServidor(params[0], regid);

                //Guardamos los datos del registro
                if(registrado)
                {
                	setRegistrationId(context, params[0], regid);
                }
            }
            catch (IOException ex)
            {
            	Log.d(TAG, "Error registro en GCM:" + ex.getMessage());
            }

            return msg;
        }
}

Lo primero que haremos será obtener una instancia del servicio de Google Cloud Messaging mediante el método GoogleCloudMessaging.getInstance(). Obtenido este objeto, el registro en GCM será tan sencillo como llamar a su método register() pasándole como parámetro el Sender ID que obtuvimos al crear el proyecto en la Consola de APIs de Google. Esta llamada nos devolverá el registration_id asignado a nuestra aplicación.

Tras el registro en GCM debemos también registrarnos en nuestro servidor, al que al menos debemos enviarle nuestro registration_id para que nos pueda enviar mensajes posteriormente. En nuestro caso de ejemplo, además del código de registro vamos a enviarle también nuestro nombre de usuario. Como ya dijimos este registro lo vamos a realizar utilizando el servicio web que creamos en el artículo sobre la parte servidor. La llamada al servicio web es análoga a las que ya explicamos en el artículo sobre servicios web SOAP por lo que no entraré en más detalles, tan sólo veamos el código.

private boolean registroServidor(String usuario, String regId)
{
	boolean reg = false;

	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(TAG, "Registrado en mi servidor.");
			reg = true;
		}
	}
	catch (Exception e)
	{
		Log.d(TAG, "Error registro en mi servidor: " + e.getCause() + " || " + e.getMessage());
	}

	return reg;
}

Por último, si todo ha ido bien guardaremos los nuevos datos de registro (usuario, registration_id, version de la aplicación y fecha de caducidad) como preferencias compartidas. Lo haremos todo dentro del método setRegistrationId().

private void setRegistrationId(Context context, String user, String regId)
{
    SharedPreferences prefs = getSharedPreferences(
	MainActivity.class.getSimpleName(),
        Context.MODE_PRIVATE);

    int appVersion = getAppVersion(context);

    SharedPreferences.Editor editor = prefs.edit();
    editor.putString(PROPERTY_USER, user);
    editor.putString(PROPERTY_REG_ID, regId);
    editor.putInt(PROPERTY_APP_VERSION, appVersion);
    editor.putLong(PROPERTY_EXPIRATION_TIME,
	System.currentTimeMillis() + EXPIRATION_TIME_MS);

    editor.commit();
}

La forma de guardar los datos mediante preferencias compartidas ya la comentamos en detalle en el artículo dedicado a las Shared Preferences. Lo único a comentar es la forma de calcular la fecha de caducidad del código de registro. Vamos a calcular esa fecha por ejemplo como la actual más una semana. Para ello obtenemos la fecha actual en milisegundos con currentTimeMillis() y le sumamos una constante EXPIRATION_TIME_MS que hemos definido con el valor 1000 * 3600 * 24 * 7, es decir, los milisegundos de una semana completa.

Y con esto habríamos terminado la fase de registro de la aplicación. Pero para recibir mensajes aún nos faltan dos elementos importantes. Por un lado tendremos que implementar un Broadcast Receiver que se encargue de recibir los mensajes, y por otro lado crearemos un nuevo servicio (concretamente un Intent Service) que se encargue de procesar dichos mensajes. Esto lo hacemos así porque no es recomendable realizar tareas complejas dentro del propio broadcast receiver, por lo que normalmente utilizaremos este patrón en el que delegamos todo el trabajo a un servicio,  y el broadcast receiver se limitará a llamar a éste.

En esta ocasión vamos a utilizar un nuevo tipo específico de broadcast receiver, WakefulBroadcastReceiver, que nos asegura que el dispositivo estará «despierto» el tiempo que sea necesario para que termine la ejecución del servicio que lancemos para procesar los mensajes. Esto es importante, dado que si utilizáramos un broadcast receiver tradicional el dispositivo podría entrar en modo de suspensión (sleep mode) antes de que termináramos de procesar el mensaje.

Crearemos por tanto una nueva clase que extienda de WakefulBroadcastReceiver, la llamamos GCMBroadcastReceiver, e implementaremos el evento onReceive() para llamar a nuestro servicio de procesamiento de mensajes, que recordemos lo llamamos GCMIntentService. La llamada al servicio la realizaremos mediante el método startWakefulService() que recibirá como parámetros el contexto actual, y el mismo intent recibido sobre el que indicamos el servicio a ejecutar mediante su método setComponent().

public class GCMBroadcastReceiver extends WakefulBroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        ComponentName comp =
        	new ComponentName(context.getPackageName(),
                GCMIntentService.class.getName());

        startWakefulService(context, (intent.setComponent(comp)));

        setResultCode(Activity.RESULT_OK);
    }
}

Para el servicio crearemos una nueva clase GCMIntentService que extienda de IntentService (para más información sobre los Intent Service puedes consultar el artículo dedicado a ellos) y como siempre implementaremos su evento onHandleIntent(). Aquí lo primero que haremos será nuevamente obtener una instancia a los Servicios de Google Play, y posteriormente obtener el tipo de mensaje recibido (mediante getMessageType()) y sus parámetros (mediante getExtras()). Dependiendo del tipo de mensaje obtenido podremos realizar unas acciones u otras. Existen algunos tipos especiales de mensaje (MESSAGE_TYPE_SEND_ERROR, MESSAGE_TYPE_DELETED, …) para ser notificado de determinados eventos, pero el que nos interesa más será el tipo MESSAGE_TYPE_MESSAGE que identifica a los mensajes «normales» o genéricos de GCM. Para nuestro ejemplo, en caso de recibirse uno de estos mensajes simplemente mostraremos una notificación en la barra de estado llamando a un método auxiliar mostrarNotificacion(). La implementación de este último método tampoco la comentaremos en detalle puesto que tenéis disponible un artículo del curso especialmente dedicado a este tema.

public class GCMIntentService extends IntentService
{
	private static final int NOTIF_ALERTA_ID = 1;

	public GCMIntentService() {
	        super("GCMIntentService");
    	}

	@Override
    	protected void onHandleIntent(Intent intent)
	{
        	GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);

        	String messageType = gcm.getMessageType(intent);
        	Bundle extras = intent.getExtras();

        	if (!extras.isEmpty())
        	{
            		if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType))
            		{
            			mostrarNotification(extras.getString("msg"));
            		}
        	}

        	GCMBroadcastReceiver.completeWakefulIntent(intent);
    	}

	private void mostrarNotification(String msg)
	{
		NotificationManager mNotificationManager =
				(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

		NotificationCompat.Builder mBuilder =
			new NotificationCompat.Builder(this)
				.setSmallIcon(android.R.drawable.stat_sys_warning)
				.setContentTitle("Notificación GCM")
				.setContentText(msg);

		Intent notIntent =  new Intent(this, MainActivity.class);
		PendingIntent contIntent = PendingIntent.getActivity(
				this, 0, notIntent, 0);

		mBuilder.setContentIntent(contIntent);

		mNotificationManager.notify(NOTIF_ALERTA_ID, mBuilder.build());
    	}
}

Sí es importante fijarse en que al final del método onHandleIntent(), tras realizar todas las acciones necesarias para procesar el mensaje recibido, debemos llamar al método completeWakefulIntent() de nuestro GCMBroadcastReceiver. Esto hará que el dispositivo pueda volver a entrar en modo sleep cuando sea necesario. Olvidar esta llamada podría implicar consumir rápidamente la batería del dispositivo, y no es lo que queremos, verdad?

Pues bien, hemos terminado. Ya tenemos nuestro servidor y nuestro cliente GCM preparados.  Si ejecutamos ambas y todo ha ido bien, introducimos un nombre de usuario en la aplicación Android, pulsamos «Registrar» para guardarlo y registrarnos, seguidamente desde la aplicación web introducimos el mismo nombre de usuario del cliente y pulsamos el botón «Enviar GCM», en pocos segundos nos debería aparecer la notificación en la barra de estado de nuestro emulador como se observa en la imagen siguiente:

notificacion-gcm

Es conveniente utilizar un emulador en el que se ejecute una versión de Android 4.2.2 o superior, dado que en versiones anteriores Google Play Services podría no funcionar. Aún así, en ocasiones los mensajes tardar varios minutos en recibirse, por lo que tened algo de paciencia.

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

También te puede interesar

44 comentarios

Luis 21/08/2013 - 19:45

Hola, mira que hasta ahora he podido hacer que todo me funcione, sin descargar codigo, pero ahora estoy atorado en que no me reconoce la libreria WakefulBroadcastReceiver.

Incluso ese proyecto si lo he descargado tal cual y lo he probado tanto en la version 18 y 17 del sdk. Pero me sigue sn reconocer la libreria.

Sabes que puede ser??

Como nota: estoy trabajando en Android Studio y no en eclipse

Responder
Luis 22/08/2013 - 8:09

Hola, al final creé la clase WakefulBroadcastReceiver manualmente y funciona todo correcto. Excelente aporte!!

Gracias!

Responder
Integración con Google+ (I): Google+ Sign-In | sgoliver.net blog 22/08/2013 - 20:08

[…] igual que ocurría con las apis de mapas o mensajería push, para hacer uso de la API de integración con Google+ necesitaremos crear un nuevo proyecto en la […]

Responder
Antonio 26/08/2013 - 11:56

Hola,
He visto tu código y es genial, solo me preguntaba por qué usas al inicio del código el registro a la «antigua» en vez de usar el nuevo método mas sencillo que propociona la clase GoogleCloudMessaging.

GoogleCloudMessaging gcm = GoogleCloudMessaging .getInstance(context);
String registrationId = gcm.register(PROJECT_NUMBER);

Por lo demás, como decia, genial.
Saludos

Responder
admin 26/08/2013 - 17:51

Hola Antonio, no sé exactamente a lo que te refieres, en mi código el registro se realiza dentro de la tarea asíncrona TareaRegistroGCM, y se hace exactamente como tú indicas, utilizando el método register() de la clase GoogleCloudMessaging. ¿A qué otro registro te refieres? Saludos.

Responder
Francisco 28/08/2013 - 17:19

Hola, sabes que tengo el problema que al ejecutar el codigo cliente de android y realizar el registro lanza el siguiente mensaje: Registro GCM no encontro y posterior a esto Unable to start service Intent { act=com.google.android.c2dm.intent.REGISTER pkg=com.google.android.gms (has extras) }: not found, y despues Error registro en GCM:SERVICE_NOT_AVAILABLE, he tratado de solucionarlo y no he podido. Agregue una cuenta al emulador, verifique la url pero no funciona.

Espero que me puedas ayudar.

Saludos

Responder
David Collado 29/08/2013 - 19:20

Gran artículo Salvador, gracias. Sin embargo me resulta más fácil e intuitiva la forma que ha quedado obsoleta, ya que adaptar mi aplicación a esta nueva versión está resultando una tarea tediosa y un poco complicada. Pero hay que moverse o caducar ;-) Un saludo.

Responder
Aure 05/09/2013 - 14:57

Gran artículo, muy bien explicado y funciona a la perfección :)
Lo único que no he conseguido que funcione es el método unregister() para borrar el registro en el servicio gsm

Responder
David 11/09/2013 - 8:06

En primer lugar muchas gracias y enhorabuena por el curso en general, sirve de muchísima ayuda.

He estado probando las notificaciones y he encontrado un comportamiento un poco extraño. Al llamar a gcm.register(id); me salta una exepción IOException:SERVICE_NOT_AVAILABLE.

Leyendo otros comentarios vi una modificación un poco extraña que me ha funcionado. Consiste en añadir al receiver otro filtro en el fichero AndroidManifest.xml (Según veo se añadía en la versión anterior):

Y así, tras la llamada a register, que me sigue fallando, el BroadcastReceiver recibe el registration_id en intent.getExtras().getString(«registration_id»);

Las pruebas las hago en un dispositivo real con versión 3.2.1. ¿Alguna idea?

Un saludo

Responder
Carlos 03/10/2013 - 22:41

Si tengo una aplicacion que ya usaba el ejemplo anterior ya no funcionara?, me toca cambiarlo?

Responder
Jhorman Delgado 07/11/2013 - 23:59

Excelente Articulo. 5 estrellas. Felicitaciones

Responder
paticio 30/11/2013 - 19:41

Estimado, realice una app en la pagina ibuildapp, y ahi hay una opcion para realizar notificaciones con este metodo, pero algo sucede que cuando coloco el Project ID y el Server API key no queda configurado para realizar las notificaciones, es posible me puedas ayudar?

muchas gracias

Responder
Vanesa 18/12/2013 - 9:57

Nada ME RINDO, ya no se que hacer, he echo tantas pruebas que he perdido la cuenta, debe ser que no se incluir la librería de google, os pongo los pantallazos para que veáis los errores a ver quien me puede ayudar OS LO RUEGO:

https://www.dropbox.com/s/966zthy3ldfaonh/Captura%20de%20pantalla%202013-12-18%20a%20las%2009.32.42.png

https://www.dropbox.com/s/uwsdnuh1qqwcb61/Captura%20de%20pantalla%202013-12-18%20a%20las%2009.41.51.png

Responder
David 28/12/2013 - 5:02

Vanesa,

Después de mucho luchar, a mi me funcionó el ejemplo.
La diferencia que veo en esas dos pantallas que pasaste, son:
* En la carpeta libs te falta incluir el archivo gcm.jar. Te subí el que tengo, espero sirva: https://mega.co.nz/#!SA5FSAqa!BkyKlGuMZ0gnj-zT2YBKOw0PVA1y7hB8CMbQ7ezjBh8

* El import lo tengo así: import com.google.android.gcm.GCMBaseIntentService;

Suerte

Responder
Pedro 08/01/2014 - 12:54

Estoy intentando descargar el código, pero al descargar el repositorio con github (para windows) se me descargan una serie de proyectos, pero la carpeta «android-new-gcm» no se me descarga, en cambio «android-gcm» si que se me descarga. ¿A alguien más le pasa? ¿Cómo lo habéis solucionado?

Responder
JESUS PEREZ 15/02/2014 - 13:22

Hola. soy nuevo en estos y me saltan algunas dudas. Si puedes dame un poco de luz en este asunto por favor.

En primer lugar me gustaría tener algun ejemplo Implementación servidor, oeri en html asp lo mas plano posible.

Responder
Aaron 21/02/2014 - 2:41

GCM tiene limite de notificaciones a mandar por dia, semana o mes?
Como le hacen facebook, o whatsapp si no usan gcm para mandar sus mensajes??

Responder
Manuel 19/03/2014 - 16:44

Buenas, es posible (llevo horas buscando cómo y no lo encuentro) el que el cliente al recibir la notificación abra una activity sin tener que pulsar sobre ella. Es decir tal y como recibe la notificación lanzar el activity.

Si teneis algún código de ejemplo os lo agradecería mucho.

Muchas gracias! de antemano.

Responder
alvaro 24/03/2014 - 6:46

buenas noches, he peleado con esto pero necesito el codigo en php, ya coloque el sender id pero he probado php y no me sirven¡, alguno tiene el codigo en php para solo colocar el api_key que tenga que ver con este ejercicio? gracias

Responder
Juan Pablo 02/04/2014 - 22:20

Buenas tardes, te agradezco por tus excelentes tutoriales.

He estado siguiendo el ejemplo pero cuando intento obtener el id del GCM en el cliente
(en la línea regid = gcm.register(SENDER_ID);)
me lanza la excepción
«SERVICE_NOT_AVAILABLE»

he visto que varios han tenido este problema pero las soluciones que aportan no me ha ayudado a corregirlo. te agradecería si pudieras darme un ayuda con esto, por que no se que mas hacer.

gracias por tu atención.

Responder
Mario Mendieta 10/04/2014 - 0:35

Buenas tardes, quería ver si alguien me podría ayudar, probé este mismo proyecto descargándolo desde github, pero no me funciona el broadcast para realizar la notificación en versiones inferiores a android 4.1, he hecho pruebas en android 4.0, 2.3.5, 2.3.6 en estas versiones no funcionaron, pero en 4.1.2 y en 4.2.2 si me han funcionado, es el mismo código representado aquí lo único que cambie fue el Sender ID.

Responder
Mario Mendieta 10/04/2014 - 18:03

Buenas tardes, solo quería confirmar que resolvi mi problema, era algo de permisos en la conectividad a internet, los dispositivos estaban conectados en diferentes conexiones de wifi, y al revisar esto pues todo funciono de manera perfecta.

Muchas Gracias.

Responder
Holly 26/04/2014 - 19:45

Hola, despues de agradecerte infinitamente por tus aportes, te comento dos dudas:

1- En esta linea mostrarNotification(extras.getString(«msg»)); entiendo que en extras venga este campo «msg» con su valor, cierto? a mi este msg me devuelve vacio…puede que sea problema en mi backend de como envio el mensaje al movil?

2- puede que este relacionado con lo de arriba, no se, pero en mi notiticacion de la barraa no me aparece el mensaje enviado al usuario, ni tampoco el Timestap ni la hora justo a la derecha de «Notificacion GCM»…que podria ser????

Gracias de antemano…

Responder
Andres Gonzalez 16/05/2014 - 16:19

Hola sinceramente no se para que sirve el google cloud messaging. ojalá me expliquen gracias

Responder
William Cuervo 19/06/2014 - 15:55

sgoliver Muchas gracias por los tutoriales. tengo una duda, ¿como hago para que al pulsar la notificación desaparezca?.

Responder
Roberto 01/07/2014 - 6:03

Hola tengo una pregunta sobre este tema, eh estado trabajando en una app para aprender sobre como funciona este servicio de google y hasta el momento me funcionado bien pero…. cuando instalo la app en diferentes dispositivos siempreme da el mismo «registration id» se cual sea el dispositivo esto es normal? o me debería de dar una clave diferente por dispositivo? Saludos y gracias por la respuesta.

Responder
Roberto 27/07/2014 - 18:35

Gracias por el tuto, parece bastante completo, pero la verdad, se hace dificil seguirlo entre otras cosas por que usas muchas variables que no estan declaradas dentro del codigo que dejas…

No es dificil saber como declararlas, para usarlas, pero de primeras se hace complicado

Un saludo

Responder
Douglas 24/09/2014 - 15:50

Realmente me funciono en la nueva herramienta de google, Android studio, con el cambio de que en vez de agregar el gcm.jar es solo agregar la referencia de google play services mayor a la versión 3 en el gradle: «com.google.android.gms:play-services:3.+» y el API Key sin la huella SHA1, de esa manera funciona con Android Studio…

Solo tengo un inconveniente y este persiste en Android Studio o en Eclipse y también si el web service es en C# o JSON y es que los caracteres especiales como la «ñ» y las vocales tildadas se envían bien de el web service a los servidores de GCM pero al dispositivo no llegan, esto significa que si envió un atributo de tipo «&data.message=español…» cuando lo recibo en el dispositivo llega como «espaol»…

No se quien tenga información de como solucionar esto…

Responder
Pablo 05/10/2014 - 21:41

Mi problema esta al enviar la notificacion desde mi servidor al servidor de google, mi aplicacion android se activa en el metodo onReceive de la clase GCMBroadcastReceiver.

Pero el telefono no muestra la notificacion

He puesto puntos de para en la clase GCMIntentService (donde pinta la notificacion pero nunca es llamado)

Que me esta fallando?

Responder
Miguel 07/10/2014 - 16:53

Hola Mario.

Podias explicar mejor la solución del problema, ya que me encuentro con el mismo problema que tuviste tú, las notificaciones no me funcionan para versiones 2.X.X y sin embrago si me funcionan para 4.X.X

Responder
Sergio 26/12/2014 - 18:36

Hola muy buenas. Excelente tu curso. Solo una pregunta:

Una vez obtenido el codigo de registro (gcm.register(…)), tu lo almacenas en el SharedPreferences; bien, yo deseo que la clase Asynctask me devuelva ese String para poder usarlo fuera en una serie de chequeos.

¿Como lo recojo? No hay manera de que me lo devuelva, siempre es nulo fuera de la clase privada.

Un saludo.

Responder
Eloy 10/02/2015 - 16:57

Hola Salvador, buenos días.

He seguido tu ejemplo, pero he cambiado algunas cosillas y no reciben las notificaciones (pero quisiera mantener los cambios).
Lo que he hecho ha sido que la clase GCMIntentService es una clase abstracta y cuando hace

Intent notIntent = new Intent(this, MainActivity.class);

en el método mostrarNotificación, ahora hace esto otro:

Intent notIntent = getNotificationIntent();

Donde getNotificationIntent(); es un método abstracto y, al ser extendida la clase (la clase es TestReciver), se devuelve un intent… en este caso es similar a lo que había (de momento estoy haciendo pruebas):

return new Intent(this, MainActivity.class);

El caso es que me pregunto como cambia esto la situación en el manifest… teniendo en cuenta que pertenecen a packages diferentes:

GCMIntentService y GCMBroadcastReceiver pertenecen a com.notification.receiver, mientras que TestReciver y MainActivity están en com.test.TestReciver.

Aquí te dejo un enlace a mi google drive donde simplemente hay un pequeñísimo diagrama de clases (de las tres afectadas) y el manifest…

Espero que me puedas hechar una mano… .Gracias.

Responder
Eloy 10/02/2015 - 16:59 Responder
Eloy 11/02/2015 - 15:43

Vale… conseguido.

El problema que tenía era que en la clase GCMBroadcastReceiver el método onReceive utiliza esto: GCMIntentService.class.getName() que es muy ummmm estático, lo cual no me sirve, ya que necesito que sea modificable desde fuera de esa clase. Tengo dos ideas en mente:

1) Hacer esta clase abstracta y quien la extienda defina un método que sea getClassName y así se le pasa el nombre de la case que descienda de GCMIntentService que, como digo en el mensaje de arriba, también es abstracta (con un método getNotificationIntent donde se devuelve un Intent creado con la Activity que se desea que se «dispare» al aceptar la notificación)

2) En lugar de clase abstracta, debería de consultar las SharePreferences en busca de un string previamente definido con el nombre de la case que descienda de GCMIntentService (a partir de aquí sería igual que el otro método).

La primera solución está probada y funciona… habría que probar la segunda (pero no sé si está funcionaría por lo de necesitar una Activity en el constructor del SharedPreferences).

Un saludo.

Responder
Curso Programación Android por Salvador Gómez – Indice de Contenidos | Miguel Moyetones 17/06/2015 - 17:55

[…] Implementación del Cliente Android (Nueva Versión) […]

Responder
Pamela 03/08/2015 - 16:52

Hola, yo e hecho un código muy parecido el cual me funciona, el problema o duda que tengo es lo siguiente, cuando lanzo la app lo hago directamente en un movil android entonces se registra el id luego desde un jsp envío la notificación y llega perfectamente, luego lanzo la app en otro movil android y también se registra el id en el jsp envío otra notificación y solo llega al segundo equipo ya no en el primero, si vuelvo a registrar el id del primero entonces envía ahi y así, nose realmente si esto es correcto, en realidad lo que yo quisiera es enviar la notificación a varios móviles alguna ayuda? les agradecería

Responder
Rafa 18/11/2015 - 12:48

Buenas tardes,

En primer lugar, muy buen artículo, me ha funcionado a la perfección. Sólo tengo un pequeño problema, es el siguiente: Si no tengo internet y me envían una notificación, cuando conecto internet me llega. Sin embargo si fuerzo el cierre de la app, me envían una notificación y abro la app, no me llega… Supongo que será algo que faltaría por hacer, ya que en apps como WhatsApp por ejemplo, eso no ocurre.

Muchas gracias de antemano,
Un saludo.

Responder
Cruz 10/01/2016 - 22:00

Primero que nada, muchas gracias por tus artículos tan impresionantes. Solo que me sale un detalle y me gustaría que me ayudaras por favor.

El problemas es que cuando mando la notificación, al llegar al dispositivo, me aparece que la aplicación se ha detenido y la notificacion no aparece en la barra de estado, solo se detiene y ya, y tengo el código exactamente como tu lo pusiste y no se que me pueda estar fallando, de antemano gracias

Responder
Cuartas Juan 27/01/2016 - 22:01

Primero que todo, excelentes tus tutoriales.
pero lo hago y no me funciona, según tengo entendido ya actualizaron todo. podrias hacerlo con el codigo nuevo?
Gracias.

Responder
Alan Saracho 03/02/2016 - 15:46

gcm.register esta deprecado. La nueva solucion es utilizar InstanceID.

Saludos!

Responder
Matias 26/04/2016 - 15:59

Hola, tengo implementado GCM en mi App.

Mi consulta es:
El GCM ID de registro se actualiza con regularidad. Entonces, ¿cuál sería la solución para mantener el ID de registro actualizado en mi servidor de envíos push sin que el usuario tenga que presionar un botón de registro cada vez?

– Se me ocurre programar mi aplicación para ejecutar el registro de GCM en el OnCreate de la actividad principal, pero sólo se actualizará cuando la aplicación se abre?, no estoy seguro de que sea la forma adecuada.
– También se me ocurre realizar una tarea automática diariamente para que realice la actualización de registro ID de GCM.

Me gustaría recibir alguna sugerencia, cual sería la forma correcta de realizarlo?

Saludos!

Responder
fernando octavio 06/07/2016 - 21:28

excelente tuto
me ha servido de mucha ayuda ,ahora puedo mandar las notificaciones pero al hacer doble click sobre ellas no pasa nada ,me podrían explicar como hacer que al pulsar en la notificación se abra un activity

Responder
Silvia 16/09/2018 - 14:27

Muchísimas gracias Salvador por este post

Llevaba unos días probando cosas distintas
y al aplicar estos pasos por fin lo he conseguido

Responder
How to send location of the device on server when needed - Tutorial Guruji 09/07/2021 - 11:32

[…] Notificaciones Push Android: Google Cloud Messaging (GCM). Implementación Cliente (Nueva Versión) (spanish) […]

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