Firebase Cloud Messaging: Notificaciones en Android
Por amunoz el Lunes, 18 de Septiembre de 2017, 17:29 - Enlace permanente
Muchas veces tenemos que añadir un sistema de notificaciones en nuestra aplicación Android. Google proporciona un sistema muy completo para esto, en los últimos tiempos han cambiado un par de veces, por eso, con la última versión a día de hoy voy a recopilar lo que hace falta para implementar las notificaciones push con Firebase Cloud Messaging.
Lo primero de todo es crear un proyecto en la consola de Firebase, para ello nos vamos a https://console.firebase.google.com/ e iniciamos sesión con nuestra cuenta de Google, una vez hayamos accedido pulsamos en "Añadir proyecto".
Nos aparecerá la siguiente pantalla donde tendremos que introducir el nombre de nuestro proyecto y seleccionar nuestro país o región.
Ahora pulsamos en "Añadir Firebase a tu proyecto Android" e introduciremos el nombre del paquete de la aplicación, un nombre (opcional) y el certificado de firma SHA1. Este certificado de firma lo puedes obtener siguiendo las instrucciones de este post.
Cuando ya tengamos todo completo pulsaremos en "REGISTRAR LA APLICACIÓN", esto generará un fichero de configuración para Firebase que tendremos que descargar y habrá que añadirlo al proyecto de Android. Pulsamos en “Descargar google-services.json”y lo ponemos en la carpeta de la aplicación del proyecto, por ejemplo, dentro de "proyecto\app\".
Ahora añadiremos las librerías de Firebase a nuestro proyecto Android: En el build.gradle de proyecto añadiremos:
buildscript { dependencies { classpath 'com.google.gms:google-services:3.1.0' } }
En el build.gradle de aplicación añadiremos:
dependencies { ... compile 'com.google.firebase:firebase-messaging:11.0.4' } // añadir al final del fichero apply plugin: 'com.google.gms.google-services'
Sincronizamos los cambios de estos dos ficheros y compilamos.
Nota: La versión de los componentes variarán con el tiempo, puedes ver cómo actualizarlas aquí.
Implementación de los servicios de Firebase para notificaciones
Ahora necesitamos declarar dos servicios, uno que extienda de FirebaseMessagingService para poder recibir notificaciones en segundo plano y otro que extienda de FirebaseInstanceIdService para gestionar los token de registro en el servicio de notificaciones. Declaramos los dos servicios dentro del tag <application> de nuestro fichero manifest añadiendo:
<!-- FCM --> <service android:name=".FCMMessagingService"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> <service android:name=".FCMListenerService"> <intent-filter> <action android:name="com.google.firebase.INSTANCE_ID_EVENT" /> </intent-filter> </service>
Como verás, en este caso las clases las he llamado FCMMessagingService y FCMListenerService.
FCMMessagingService
Como ya he comentado antes, este servicio se encarga de recibir las notificaciones en segundo plano, la siguiente implementación obtendrá datos de esa notificación y la mostrará en la barra de notificaciones. Creamos el fichero FCMMessagingService.java en el paquete principal con el siguiente contenido:
import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.support.v4.app.NotificationCompat; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; import org.json.JSONObject; import java.util.Map; import java.util.Random; public class FCMMessagingService extends FirebaseMessagingService { private static int getNotificationIcon() { boolean useWhiteIcon = (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP); return useWhiteIcon ? R.mipmap.icon_silhouette : R.mipmap.ic_launcher; } @Override public void onMessageReceived(RemoteMessage message) { if (message.getNotification() != null) { String mensaje = message.getNotification().getBody(); String title = message.getNotification().getTitle() != null ? message.getNotification().getTitle() : getString(R.string.app_name); NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext()) .setSmallIcon(getNotificationIcon()).setContentTitle(title) .setStyle(new NotificationCompat.BigTextStyle().bigText(mensaje)).setContentText(mensaje); Intent notificationIntent = new Intent(getApplicationContext(), ListadoTrabajosActivity_.class); notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); mBuilder.setContentIntent(contentIntent); Notification notification = mBuilder.build(); notification.flags |= Notification.FLAG_AUTO_CANCEL; notification.flags |= Notification.DEFAULT_SOUND; notification.defaults |= Notification.DEFAULT_SOUND; notification.defaults |= Notification.DEFAULT_VIBRATE; Random randomGenerator = new Random(); int randomInt = 0; for (int idx = 1; idx <= 10; ++idx) { randomInt = randomGenerator.nextInt(100); } notificationManager.notify(randomInt, notification); } } }
En la primera función se elige entre icono de silueta o el icono de la aplicación ya que a partir de Android Lollipop el icono de la notificación debe ser un icono de silueta, este icono lo puedes generar desde aquí: https://romannurik.github.io/AndroidAssetStudio/icons-notification.html
FCMListenerService
Esta clase será la encargada de recibir el token (esto ocurrirá al iniciar la aplicación). Este token tendremos que enviarlo a nuestro servicio web y, en su caso, almacenarlo asociado al usuario para enviarle notificaciones personalizadas.
import android.util.Log; import com.google.firebase.iid.FirebaseInstanceId; import com.google.firebase.iid.FirebaseInstanceIdService; public class FCMListenerService extends FirebaseInstanceIdService { public static String getToken() { return FirebaseInstanceId.getInstance().getToken(); } public void onTokenRefresh() { //Enviar token al servidor para almacenarlo Log.d("PRUEBA", "Token: " + getToken()); } }
Si todo va bien, al iniciar la aplicación veremos en el Monitor de Android algo parecido a esto
Token: Av3_dZi4yuw:APA91bGXtDv36eMEgAp4f3Ft7QAJ1nCMYTiiMrKwfh7MzOeNrwbabxuQ57pTzqbLF5Oam4CYXWIHEPqKv35qm3qK-8k8Cn-JegNQExhwoQfGYX_GMxJCGcORPSKymeezRBscIgoqLNag
Este será el token correspondiente a esta instalación de la aplicación para este dispositivo. Si vuelves a ejecutar la aplicación no aparecerá, ya que este método solo se ejecuta cuando hay cambios en el token, podrás obtener este valor en cualquier momento mediante FCMListenerService.getToken().
Envío de notificaciones
Para enviar notificaciones podemos probar mediante la consola de Firebase, esta utilidad se encuentra en el menú de la izquierda, apartado AMPLIACIÓN > Notifications
Aparecerá una pantalla donde podremos escribir nuestro mensaje, seleccionaremos "Un único dispositivo", pegamos nuestro token en el campo correspondiente y pulsamos "Enviar mensaje".
Al enviar veremos en la aplicación el mensaje escrito:
Envío manual de notificaciones
Si queremos enviar las notificaciones mediante nuestros programas necesitaremos la APIKey de nuestra aplicación y el token del dispositivo que ya hemos obtenido en la aplicación Android.
La APIKey la podemos ver en la consola de Firebase accediendo a la configuración de nuestro proyecto, apartado "Mensajería en la nube":
La APIKey de nuestra aplicación que podremos usar serán cualquiera de las dos que he marcado en la imagen y bastaría con ejecutar el siguiente código PHP para recibir la notificación (cambiando el token y la clave del servidor):
<?php //$tokenDispositivo = 'TOKEN_ANDROID'; // Usar para un único dispositivo $tokens = ['TOKEN_ANDROID_1', 'TOKEN_ANDROID_2']; $payloadNotificacion = [ 'body' => 'Prueba de mensaje', 'title' => 'Título', 'sound' => 'default', 'vibrate' => 'true' ]; $campos = [ //'to' => $tokenDispositivo, // Usar para un único dispositivo 'registration_ids' => $tokens, // Usar para varios destinatarios 'notification' => $payloadNotificacion ]; $cabeceras = [ 'Authorization: key='.'CLAVE_DEL_SERVIDOR', 'Content-Type: application/json', ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://fcm.googleapis.com/fcm/send'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $cabeceras); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($campos)); $resultado = curl_exec($ch); curl_close($ch); echo $resultado; ?>
Como verás en el código se puede enviar la notificación a un único dispositivo o una misma notificación a varios dispositivos.
Esto es todo, ya podéis enviar notificaciones sin parar!
Saludos!
Comentarios
Que tal buenas tardes, excelente aporte amigo, disculpa tengo un problema al querer recibir una notificación cuando la app no esta en primer plano, en android 5, me podrias orientar me manda el error Couldn't create icon: StatusBarIcon ojala puedas ayudarme. Saludos
Hola César, según he visto por Internet puede ser que estés usando un xml (drawable) como icono para las notificaciones y Android 5 no lo soporta.
Deberías usar un icono de silueta en png, intenta generar uno con este generador a ver si te funciona: https://romannurik.github.io/Androi...
Saludos!
Muchas gracias amunoz me ayudo mucho la ayuda y la explicación del problema que me estaba pasando, ya funciona correctamente.
Hola que tal oye una pregunta, no se si me puedas ayudar al recibir notificaciones cuando el app esta en segundo plano me llega la notificacion pero en error y al momento de abrirla no hace la accion que le programe. no se puedas asesorar con eso es con un android oreo. bueno gracias