Notificaciones de Android: recurrencia, insignias y gamificación
Las notificaciones en un gestor de tareas son críticas. Si el recordatorio no llega en el momento adecuado, la aplicación ha fallado. Y en Android, hacer que un recordatorio llegue en el momento adecuado es una batalla constante contra el propio sistema operativo.
No es un problema de código. Es un problema de ecosistema. Samsung, Xiaomi, Huawei, Oppo: cada fabricante implementa su propia política de ahorro de batería que puede matar tu aplicación y sus notificaciones programadas sin previo aviso. Añade las insignias de icono, las notificaciones push de FCM y las notificaciones de gamificación, y obtendrás un sistema que debe ser fiable, compatible con múltiples fabricantes y emocionalmente satisfactorio a la vez.
Puntos clave
- Los recordatorios recurrentes programan las próximas 30 ocurrencias de una vez para sobrevivir a la eliminación de Android, en lugar de programar solo la próxima ocurrencia.
- Cada vez que se abre la aplicación, se vuelven a verificar y reprogramar las notificaciones perdidas durante el reinicio o la eliminación por parte del sistema.
- Las insignias de icono en Android requieren implementaciones específicas por lanzador (Samsung, Nova, AOSP), sin una API unificada.
- Las notificaciones de gamificación transforman un mecanismo del sistema en un momento de motivación gracias a mensajes personalizados e iconos personalizados.
¿Por qué programar 30 ocurrencias en lugar de una sola?
La respuesta se resume en una palabra: supervivencia. En Android, el sistema operativo puede decidir cerrar tu aplicación en cualquier momento para liberar memoria. Cuando la aplicación se cierra, la lógica JavaScript que debía programar la siguiente ocurrencia muere con ella.
La estrategia ingenua es programar solo el próximo recordatorio y reprogramar el siguiente cuando se active el primero. Problema: si la aplicación se cierra entre la activación del recordatorio y la reprogramación, la cadena se rompe. El usuario nunca volverá a recibir un recordatorio.
Mi solución: programar las próximas 30 ocurrencias tan pronto como se crea el recordatorio recurrente. Si tienes un recordatorio diario a las 9h, la aplicación crea inmediatamente 30 alarmas: una para mañana a las 9h, una para pasado mañana a las 9h, y así sucesivamente. Incluso si la aplicación se cierra durante 3 semanas, los recordatorios seguirán llegando.
La documentación de Android sobre AlarmManager recomienda usar setExactAndAllowWhileIdle para alarmas críticas. Esto es lo que uso para cada ocurrencia. El "AllowWhileIdle" es crucial: sin él, el modo Doze de Android (activado cuando el teléfono está inactivo y no conectado) puede posponer la alarma indefinidamente.
¿Por qué 30 y no 365? Porque demasiadas alarmas simultáneas pueden afectar el rendimiento y la batería. 30 días es el compromiso ideal: el usuario está cubierto durante un mes, y la aplicación actualiza las ocurrencias cada vez que se abre. Ya había explorado este tipo de compromiso rendimiento/fiabilidad en el artículo sobre la recurrencia de los recordatorios.
¿Cómo gestionar las notificaciones perdidas al reiniciar?
Cuando un teléfono se reinicia, todas las alarmas programadas a través de AlarmManager se pierden. Este es el comportamiento estándar de Android. La solución oficial: escuchar el broadcast BOOT_COMPLETED para reprogramar las alarmas al inicio.
En teoría, es limpio. En la práctica, es un campo minado.
Samsung bloquea BOOT_COMPLETED por defecto en algunos modelos con su "Optimización de la batería". Xiaomi tiene MIUI que impide el inicio automático de las aplicaciones a menos que el usuario lo autorice manualmente en la configuración. Huawei con EMUI hace lo mismo. Oppo con ColorOS, igual. Según el sitio Don't Kill My App, que enumera los comportamientos por fabricante, algunos dispositivos bloquean hasta el 90% de los broadcasts.
Mi solución pragmática: no depender únicamente de BOOT_COMPLETED. Cada vez que se abre la aplicación, el NotificationService ejecuta una auditoría completa:
- Recuperación de los recordatorios activos de la base de datos local.
- Verificación: para cada recordatorio, ¿la alarma correspondiente todavía existe en
AlarmManager? - Reprogramación de las alarmas que faltan.
- Limpieza de las alarmas obsoletas (tareas eliminadas, recordatorios desactivados).
Este proceso tarda unos 200ms para 50 recordatorios activos. El usuario no lo nota. Pero garantiza que incluso si el teléfono se ha reiniciado, incluso si el fabricante ha bloqueado el broadcast de inicio, las notificaciones se reprogramarán en la próxima apertura de la aplicación.
¿Cómo gestiona Firebase Cloud Messaging las notificaciones push de grupo?
Las notificaciones locales (recordatorios, recurrencia) son gestionadas por AlarmManager en el dispositivo. Las notificaciones push (actividad de grupo, insignias desbloqueadas, menciones) pasan por Firebase Cloud Messaging.
El flujo es el siguiente:
- Un usuario crea una tarea en un grupo y la asigna a un miembro.
- El backend detecta la asignación y recupera el token FCM del asignado de la base de datos.
- El backend envía un mensaje FCM con el título, el cuerpo y una carga útil de datos (tipo de acción, ID de la tarea, ID del grupo).
- El teléfono del asignado recibe la notificación push, incluso si la aplicación está cerrada.
- Si el usuario toca la notificación, la aplicación se abre directamente en la tarea en cuestión gracias al deep link en la carga útil.
La principal trampa con FCM: los tokens cambian. Un token FCM puede volverse inválido cuando el usuario desinstala y reinstala la aplicación, cuando borra los datos de la aplicación, o cuando Google decide reciclarlo. Mi estrategia: actualizar el token en cada inicio de la aplicación y compararlo con el almacenado en la base de datos. Si es diferente, actualizar. Y en el backend, cada error de envío de FCM con el código messaging/registration-token-not-registered activa una limpieza automática del token inválido.
Es el mismo principio de resiliencia que aplico en el sistema de seguridad y limitación de velocidad: anticipar los fallos, no solo gestionarlos cuando ocurren.
¿Por qué las insignias de icono son tan difíciles en Android?
El pequeño círculo rojo con un número en el icono de la aplicación. En iOS, es una línea de código: UIApplication.shared.applicationIconBadgeNumber = 5. Es una API de sistema estandarizada desde iOS 3.
En Android, no existe una API estandarizada para las insignias de icono. Cada lanzador implementa su propio método:
- Samsung (OneUI): utiliza los Samsung BadgeProvider a través de un ContentProvider específico.
- Nova Launcher: lee las insignias de un Intent de broadcast personalizado.
- AOSP/Pixel: utiliza el canal de notificación con
setNumber()en la propia notificación (sin insignia independiente). - Xiaomi (MIUI): tiene su propia API a través de un ContentProvider diferente al de Samsung.
- Huawei: utiliza los Huawei Mobile Services en lugar de los Google Play Services.
La biblioteca react-native-app-badge abstrae parte de esta fragmentación, pero no cubre todos los casos. He aceptado esta realidad: en algunos dispositivos, las insignias funcionarán perfectamente. En otros, no se mostrarán. La aplicación nunca debe depender exclusivamente de las insignias para comunicar información importante, son un extra visual, no un canal crítico.
¿Cómo transforman las notificaciones de gamificación la experiencia?
Las notificaciones más satisfactorias no son los recordatorios. Son las notificaciones de gamificación. Una insignia desbloqueada, un hito de racha alcanzado, un nivel subido: son momentos de celebración que el usuario no esperaba.
He implementado tres tipos de notificaciones de gamificación:
Insignia desbloqueada
Notificación local con un icono personalizado correspondiente a la insignia. El texto es personalizado: "¡Insignia Organizador Pro desbloqueada! Has creado 100 tareas." Es más atractivo que "Nueva insignia desbloqueada". La personalización del mensaje aumenta la tasa de retención de la notificación según las investigaciones de OneSignal sobre la personalización.
Hito de racha
"¡Racha de 30 días! Sigue así." La racha es un contador de días consecutivos de uso de la aplicación. El sistema reconoce hitos: 7 días, 30 días, 100 días, 365 días. Cada hito activa una notificación y puntos de bonificación. Había detallado todo el sistema de gamificación en el artículo sobre el esquema de gamificación.
Subida de nivel
"¡Nivel 5 alcanzado! Ahora eres Estratega." Los 12 niveles tienen cada uno un nombre y un umbral de puntos. La notificación de subida de nivel es la más rara y gratificante. Es el mismo mecanismo de recompensa variable que describe B.F. Skinner en sus investigaciones sobre el refuerzo: las recompensas impredecibles crean un compromiso más fuerte que las recompensas predecibles.
El punto común entre estos tres tipos: transforman una notificación (mecanismo a menudo percibido como intrusivo) en un momento positivo. El usuario no sufre la notificación, la espera. Esta es exactamente la filosofía del feed de gamificación: hacer que el compromiso sea visible y gratificante.
¿Cómo optimizar la fiabilidad de las notificaciones en todos los dispositivos?
Si tuviera que resumirlo en una frase: no confíes en nada. No confíes en BOOT_COMPLETED. No confíes en AlarmManager en modo Doze. No confíes en los tokens FCM. No confíes en las insignias de icono.
La estrategia de defensa en profundidad:
- Capa 1: alarmas locales a través de
setExactAndAllowWhileIdle(30 ocurrencias pre-programadas). - Capa 2: auditoría y reprogramación en cada apertura de la aplicación.
- Capa 3: push FCM como respaldo para recordatorios críticos (enviados por el backend si la alarma local no ha sido confirmada).
- Capa 4: guía al usuario hacia la configuración de la batería del fabricante (pantalla dedicada en la configuración de la aplicación).
Este último punto a menudo se pasa por alto. La aplicación incluye una pantalla que detecta el fabricante del teléfono y guía al usuario paso a paso para desactivar la optimización de la batería para TAMSIV. Esto es UX técnica: explicar al usuario por qué sus recordatorios podrían no funcionar y cómo solucionarlo. Es la misma transparencia que en el onboarding de registro diferido: ser honesto sobre las limitaciones en lugar de ocultarlas.
Preguntas frecuentes
¿Funcionan los recordatorios recurrentes cuando el teléfono está apagado?
No. Ninguna alarma puede activarse cuando el teléfono está apagado. Sin embargo, tan pronto como el teléfono se reinicia, la aplicación reprograma automáticamente las ocurrencias perdidas en la próxima apertura. Los recordatorios pasados no se envían retroactivamente.
¿Por qué las notificaciones a veces se retrasan en algunos teléfonos?
Esto está relacionado con el modo Doze de Android y las optimizaciones de batería de los fabricantes. TAMSIV utiliza setExactAndAllowWhileIdle para minimizar los retrasos, pero algunos fabricantes (especialmente Xiaomi y Huawei) imponen restricciones adicionales. La sección "Notificaciones" de la configuración de la aplicación explica cómo desactivarlas.
¿Se pueden desactivar las notificaciones de gamificación?
Sí. La configuración de la aplicación permite desactivar de forma independiente las notificaciones de insignias, rachas y subidas de nivel. Los recordatorios de tareas y las notificaciones push de grupo están en canales separados y no se ven afectados.
¿Se pierde la racha si me salto un día?
La racha vuelve a cero si te saltas un día. Sin embargo, el sistema de "congelación de racha" te permite proteger tu racha: si has acumulado suficientes puntos, puedes activar una congelación que preserva tu racha durante un día de inactividad.
¿Las insignias de icono muestran el número exacto de notificaciones no leídas?
En Samsung y los lanzadores compatibles, sí, la insignia muestra el número exacto. En los lanzadores AOSP estándar, la insignia es un simple punto (presencia/ausencia) sin número. Es una limitación de Android que Google aún no ha unificado.