Android y Laravel: Envío de notificaciones FCM
Tiempo de lectura: 5.71 minutos
El envío de notificaciones Android es muy importante.
Las notificaciones nos permiten informar a nuestros usuarios de eventos, a medida que estos ocurren, minutos antes de que ocurran, o incluso posteriormente en forma de recordatorio.
El envío apropiado de notificaciones se asocia con la ejecución de tareas recurrentes en el servidor, que determinan cuándo es conveniente enviar notificaciones, sin abusar de la frecuencia.
Hoy veremos cómo enviar una notificación general a todos los usuarios, desde un panel administrativo.
Este punto de partida te permitirá enviar notificaciones bajo distintas circunstancias, según los requerimientos de tu aplicación.
FCM: Firebase Cloud Messaging
Las notificaciones Android que usaremos estarán potenciadas por esta tecnología llamada FCM.
No es exclusivo de Android. FCM está disponible también para iOS, aplicaciones Web, juegos desarrollados en Unity y proyectos C++.
Vayamos a lo que nos interesa, viendo paso a paso los cambios a realizar en nuestro proyecto Android.
FCM en Android
Cuando se envía una notificación Android, ésta puede recibirse en distintos tipos de clientes. En este caso nuestra aplicación Android será un cliente, que recibirá una notificación de parte de un servidor, que hace el envío.
Como el mismo nombre lo indica (Firerbase Cloud Messaging), este es un servicio que forma parte de la suite de Firebase. Entonces lo primero es integrar Firebase a nuestro proyecto Android.
Si ya tienes Firebase en tu proyecto puedes saltar la sección siguiente, que está dedicada para aquellos que no.
Firebase en Android
Tal como se observa en la imagen, lo primero es acceder a la consola de Firebase y crear un proyecto Firebase nuevo.
Para este ejemplo voy a integrar Firebase sobre un proyecto llamado Km0.
Tras ingresar el nombre y asociar una cuenta de Google Analytics por recomendación de Firebase ya tengo el proyecto creado.
Si sigues estos pasos llegarás al Dashboard de tu proyecto. Allí debes hacer clic en el botón con el ícono de Android para iniciar con la configuración.
En este punto Firebase nos solicita el "package name" de nuestro proyecto. Si no lo recuerdas puedes encontrarlo en tu archivo AndroidManifest.xml
.
Opcionalmente también puedes agregar un Alias para el proyecto.
Y entonces Firebase te permitirá descargar un archivo de configuración. Este archivo (llamado google-services.json
) debes agregarlo a la carpeta app
de tu proyecto Android.
Por último agregamos el SDK de Firebase a nuestro proyecto y habilitamos Google Play Services.
Luego de modificar los build.gradle
a nivel de proyecto y módulo, presionamos "Sync Now" y ejecutamos nuestra aplicación nuevamente para que Firebase nos informe si nos hemos comunicado con sus servidores correctamente.
En pocos segundos recibirás la confirmación:
Aplicación Android cliente de FCM
Para que nuestra aplicación sea cliente de Firebase Cloud Messaging, empecemos agregando el SDK de FCM a nuestro proyecto.
Puedes hacer ello agregando esta línea en tu archivo build.gradle
(a nivel de Module
):
implementation 'com.google.firebase:firebase-messaging:20.1.0'
Como segundo paso editamos nuestro AndroidManifest, agregando un elemento <service>
al interior de <application>
.
<service
android:name=".io.fcm.FCMService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Esto permitirá que la clase FCMService
dentro de /io/fcm
reciba información de las notificaciones.
Es un ejemplo, por lo que el nombre de la clase y su ubicación pueden cambiar, si lo crees conveniente.
Y respecto al contenido de la clase, puedes usar esta clase FCMService.kt como guía.
En esta clase que te comparto encontrarás cosas que pueden cambiar según tu proyecto.
Por ejemplo:
- Para trabajar con preferencias usé
extension functions
de Koltin, para facilitar tanto la escritua como lectura - Y las consultas al servidor están hechas con Retrofit (es importante guardar el token en el servidor para luego enviar notificaciones a los usuarios)
Si quieres aprender más sobre estos 2 últimos te recomiendo seguir el curso que tengo publicado en Udemy, sobre Android y Laravel.
Obtener el FCM token de cada usuario
En la clase que te comentaba antes, tenemos un método onNewToken
que es invocado cada vez que el token cambia (hay un proceso de refresh que ocurre cada cierto tiempo, y es importante guardar el token para que el usuario pueda recibir notificaciones de manera específica cuando se necesite).
¿Qué hacer en este método?
override fun onNewToken(newToken: String) {
super.onNewToken(newToken)
val jwt = preferences["jwt", ""]
if (jwt.isEmpty()) return
val authHeader = "Bearer $jwt"
val call = apiService.postToken(authHeader, newToken)
call.enqueue(object: Callback<Void> {
override fun onFailure(call: Call<Void>, t: Throwable) {
toast(t.localizedMessage)
}
override fun onResponse(call: Call<Void>, response: Response<Void>) {
if (response.isSuccessful) {
Log.d(TAG, "Token registrado correctamente")
} else {
Log.d(TAG, "Hubo un problema al registrar el token")
}
}
})
}
En el ejemplo anterior, ocurre lo siguiente:
- Primero accedo a las preferencias para ver si hay un JSON Web Token (es decir, una sesión de usuario en la aplicación)
- Luego preparamos un
Authorization header
para que la petición al servidor incluya el JWT, y de esta manera el dato se pueda asociar al usuario - Por último, usamos un
API Service
para llevar a cabo la petición y registrar el token en nuestro servidor
Este código en Android es válido para cualquier tecnología backend que implemente los endpoints adecuados. Una alternativa es implementar la API con Laravel.
Existen alternativas a Laravel, como Lumen, si sólo se pretende desarrollar una API.
Pero si también tenemos un proyecto web (un panel administrativo con reportes en este caso), entonces podemos aprovechar el mismo proyecto Laravel.
Nota: en el último ejemplo notarás que usé toast
para mostrar un Toast
sin escribir una línea extensa como es usual. Eso también es gracias a la definición de una extension function
.
Registrando el FCM token en el servidor
Para que una comunicación sea segura, es importante usar el protocolo HTTPS. De esta manera los datos viajan seguros hasta llegar al servidor.
Usar JSON Web Tokens es añadir una capa adicional de seguridad, para que, en vez de enviar las credenciales de un usuario por cada solicitud al servidor, se envíe un JWT que represente su sesión.
Los JWT funcionan de manera diferente a cómo funcionan las Cookies, que se han usado tradicionalmente para representar sesiones de usuarios en un navegador.
Todo esto, lo revisamos con detenimiento en el curso de Android y Laravel. Por lo que si aún no te has inscrito, te invito a hacerlo ?
En resumen, nuestra API tendría una ruta declarada (usualmente en routes\api.php
):
Route::post('/fcm/token', 'FirebaseController@postToken');
Esta ruta o endpoint será atendida por un controlador. En este caso, a través de un método store
en FirebaseController
.
Es así que sólo debemos identificar al usuario por su JWT, y guardar su correspondiente FCM Token (el que le asigna Firebase, para poder enviarle notificaciones luego):
public function postToken(Request $request)
{
$user = Auth::guard('api')->user();
if ($request->has('device_token')) {
$user->device_token = $request->input('device_token');
$user->save();
}
}
Enviar notificación a todos los usuarios
De lado de nuestro proyecto web podemos crear un formulario con 2 campos:
La idea es que este formulario haga una petición POST
a una ruta. Por ejemplo, puedes tener una ruta como la siguiente en routes\web.php
:
Route::post('/fcm/send', 'FirebaseController@sendAll');
De tal manera, que en el método sendAll
de nuestro Admin\FirebaseController
tengamos algo como lo siguiente:
public function sendAll(Request $request)
{
$recipients = User::whereNotNull('fcm_token')
->pluck('fcm_token')->toArray();
fcm()
->to($recipients)
->notification([
'title' => $request->input('title'),
'body' => $request->input('body')
])
->send();
$notification = 'Notificación enviada a todos los usuarios (Android).';
return back()->with(compact('notification'));
}
En el ejemplo anterior obtenemos la lista de usuarios que tienen un token asociado (es decir, usuarios que han usado nuestra aplicación).
Luego, hacemos una petición a FCM, para que les envíe una notificación a todos ellos, usando el título y mensaje ingresados en el formulario.
Puedes pedirle ello a FCM de múltiples maneras. Por ejemplo, a través de una petición HTTP, enviando las credenciales de tu aplicación.
En el código de arriba todo ello se ve simplificado debido a que usé un paquete de Laravel para FCM, y las credenciales están indicadas en un archivo de configuración.
Credenciales de Cloud Messaging
El server key, que te permitirá hacer el envío de notificaciones, lo puedes encontrar yendo a Settings
y accediendo a la pestaña Cloud Messaging
.
Tal como se muestra en la imagen siguiente:
Obtener el FCM token en cualquier momento
Anteriormente comentamos que el método onNewToken
en nuestro FCMService
se llama cada vez que el token se actualiza.
¿Pero qué ocurre si un usuario cambia de dispositivo o inicia sesión por primera vez?
Para asegurar que disponemos del valor adecuado podemos acceder al token justo después de que el usuario ha iniciado sesión, y así registrar su valor.
Esto es posible de la siguiente manera:
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener(this) { instanceIdResult ->
val deviceToken = instanceIdResult.token
// Aquí es posible hacer una petición POST a nuestra API para registrar el token
}
Conclusión
Como ves, las notificaciones son un tema muy interesante y su uso está asociado a otros conceptos, tales como:
- Comunicación con un servidor web a través de una API
- Autenticación en Android usando JSON Web Tokens
La intención de este artículo es explicar en términos generales cómo funciona el envío de notificaciones push vía Firebase Cloud Messaging.
Si quieres aprender más de estos temas, te invito a seguir el curso de Laravel y Android que tengo publicado en Udemy, donde todas tus dudas serán bienvenidas.