Recordatorios fiables en Android en 2026: WorkManager, alarmas exactas y las nuevas reglas de batería
Cómo publicar en 2026 recordatorios en Android que de verdad se disparen — WorkManager vs AlarmManager, SCHEDULE_EXACT_ALARM, POST_NOTIFICATIONS y las rarezas de los fabricantes que todavía muerden.
Una aplicación de recordatorios en Android solo funciona si el recordatorio aparece. Suena obvio — hasta que publicas una. Entonces descubres el modo doze, los cubos de espera de aplicaciones (app standby buckets), los optimizadores de batería de los fabricantes, el permiso SCHEDULE_EXACT_ALARM, el permiso en tiempo de ejecución POST_NOTIFICATIONS, las restricciones de los servicios en primer plano, y el encantador hecho de que Samsung y Xiaomi matarán silenciosamente tu trabajo programado para «ahorrar batería», por muy correcto que lo hayas escrito.
Construí Hydrame, una aplicación de seguimiento de agua cuya propuesta de valor entera es «recuérdamelo en el momento justo, con suavidad». He dedicado más tiempo del que me gustaría admitir a forzar la pila de recordatorios de Android hacia la fiabilidad. Este artículo es la versión de 2026 del manual que por fin funciona.
El problema de los recordatorios en un párrafo
Android es agresivo a la hora de matar el trabajo en segundo plano, y debe serlo — sin control, cada aplicación despertaría la radio cada pocos minutos y tu batería moriría antes del almuerzo. El resultado para las aplicaciones de recordatorios legítimas es una pila de API superpuestas que se comportan cada una de forma ligeramente distinta según las versiones y las capas de los fabricantes. El trabajo consiste en elegir la primitiva adecuada para tu nivel de «debe ser exacto» y aceptar que el resto es probar en dispositivos reales.
Las dos primitivas que importan en 2026 son:
WorkManager— para trabajo inexacto y aplazable. Comprobaciones periódicas, casos de uso de «recuérdamelo en algún momento de la próxima hora». Consciente de la batería, se agrupa, se ejecuta incluso tras reinicios.AlarmManager.setExactAndAllowWhileIdle— para notificaciones exactas, visibles para el usuario, en un momento concreto. La cara. Úsala con moderación y solo cuando «desviarse 15 minutos» haría que la función pareciera rota.
Si tu recordatorio tiene que dispararse exactamente a las 19:30, necesitas una alarma exacta. Si «alrededor de las 19:30» está bien, usa WorkManager. La mayoría de las aplicaciones eligen por defecto la equivocada.
Tres permisos que tienes que acertar
En 2026, tres permisos en tiempo de ejecución/instalación deciden si el usuario llega a ver un recordatorio:
POST_NOTIFICATIONS(en tiempo de ejecución, Android 13+). Sin él, tu notificación literalmente no aparece. Pídelo en el momento adecuado, no en el primer arranque.SCHEDULE_EXACT_ALARM(Android 12+, restringido en 13+). Necesario parasetExactAndAllowWhileIdle. En Android 14+ solo se concede después de que el usuario lo active explícitamente en los ajustes del sistema; la aplicación tiene que enviar al usuario allí.USE_EXACT_ALARM(Android 13+, uso especial). Para aplicaciones donde las alarmas exactas son el núcleo (despertadores, recordatorios de calendario, hidratación). Play pide una justificación.
El manifiesto de Hydrame se ve así:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
RECEIVE_BOOT_COMPLETED es el que la mayoría olvida — sin él, tus alarmas programadas desaparecen en el siguiente reinicio. Vuelve a programarlas desde un BroadcastReceiver en ACTION_BOOT_COMPLETED.
Pedir POST_NOTIFICATIONS en el momento adecuado
No lo pidas en el primer arranque. La tasa de aceptación es muchísimo mayor si lo pides en un momento que se corresponde con el valor: justo cuando el usuario activa su primer recordatorio. Un flujo real:
@Composable
fun EnableReminderButton(onEnabled: () -> Unit) {
val context = LocalContext.current
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
) { granted ->
if (granted) onEnabled()
}
FilledButton(onClick = {
if (Build.VERSION.SDK_INT >= 33) {
val state = ContextCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS,
)
if (state == PackageManager.PERMISSION_GRANTED) onEnabled()
else launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
} else {
onEnabled()
}
}) { Text("Enable reminders") }
}
Dos cosas más que siempre hago:
- Una justificación de segunda oportunidad. Si el usuario deniega, no vuelvas a preguntar de inmediato. La próxima vez que intente activar un recordatorio, muestra un pequeño mensaje en línea con un botón que abra la pantalla de ajustes de la aplicación para que pueda activarlo él mismo.
- Detecta el estado de denegado-con-no-volver-a-preguntar recordando el último rechazo en DataStore —
shouldShowRequestPermissionRationaleno es fiable por sí solo para esto.
Inexacto: trabajo periódico de WorkManager
Para un recordatorio en el que «en algún momento de la mañana» es aceptable, WorkManager es la aburrida respuesta correcta:
class DailyHydrationCheckWorker(
appContext: Context,
params: WorkerParameters,
) : CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
scheduleTodayReminders()
return Result.success()
}
}
fun scheduleDailyHydrationCheck(context: Context) {
val request = PeriodicWorkRequestBuilder<DailyHydrationCheckWorker>(
repeatInterval = 1, repeatIntervalTimeUnit = TimeUnit.DAYS,
)
.setConstraints(
Constraints.Builder()
.setRequiresBatteryNotLow(true)
.build(),
)
.setInitialDelay(secondsUntil(6, 0).toLong(), TimeUnit.SECONDS)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"daily_hydration_check",
ExistingPeriodicWorkPolicy.UPDATE,
request,
)
}
Algunas cosas que muerden a la gente:
- Período mínimo de 15 minutos. Un
PeriodicWorkRequestno se ejecutará más a menudo que cada 15 minutos, sin importar lo que pidas. Para programaciones de menos de 15 minutos, usa trabajo puntual que se encadena a sí mismo. - Sin sincronización exacta. WorkManager programa dentro de una ventana. Si necesitas «a las 8:00:00 en punto», esta es la primitiva equivocada.
- Battery-not-low es amable con tus usuarios y con el sistema; actívala salvo que tengas una razón para no hacerlo.
Exacto: AlarmManager.setExactAndAllowWhileIdle
Cuando el recordatorio tiene que dispararse en un momento concreto — por ejemplo, un usuario dijo «recuérdamelo a las 19:30» y va en serio — necesitas una alarma exacta. El patrón:
fun scheduleExactReminder(
context: Context,
triggerAtMillis: Long,
requestCode: Int,
) {
val am = context.getSystemService(AlarmManager::class.java)
if (Build.VERSION.SDK_INT >= 31 && !am.canScheduleExactAlarms()) {
// Send the user to the system page to grant the permission.
context.startActivity(
Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)
.setData(Uri.parse("package:${context.packageName}"))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
)
return
}
val intent = Intent(context, ReminderReceiver::class.java)
.putExtra("requestCode", requestCode)
val pending = PendingIntent.getBroadcast(
context, requestCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
)
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pending)
}
setExactAndAllowWhileIdle es la que respeta el modo doze. Usa setAlarmClock en su lugar solo si tu aplicación es realmente un despertador — muestra el chip de la próxima alarma en la interfaz del sistema y obtiene la máxima prioridad.
Receiver + servicio en primer plano para el momento pesado
Cuando la alarma se dispara, tienes unos pocos segundos de tiempo despierto para publicar una notificación. Para un simple recordatorio de «¡ding!» eso basta:
class ReminderReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// Post the notification synchronously.
val nm = context.getSystemService(NotificationManager::class.java)
nm.notify(
intent.getIntExtra("requestCode", 0),
buildHydrationNotification(context),
)
// Re-schedule the next one.
scheduleNext(context)
}
}
Si necesitas hacer trabajo real en la alarma (sincronizar, descargar), promociónalo de inmediato a un servicio en primer plano. No intentes ser ingenioso — el sistema te revocará en un instante si te quedas atascado en el receiver.
Reinicios y cambios de zona horaria
Dos eventos rompen silenciosamente una implementación ingenua:
- Reinicio. Las alarmas se borran. Escucha
ACTION_BOOT_COMPLETEDy vuelve a programar desde tu estado persistente. - Cambio de zona horaria. Un recordatorio de las 7 de la mañana no debería dispararse a las 7 hora local antes de que el usuario se mudara y a las 7 hora de Estambul después. Escucha
ACTION_TIMEZONE_CHANGEDy recalcula.
Un único receiver maneja ambos:
<receiver android:name=".BootAndTimezoneReceiver" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.TIMEZONE_CHANGED"/>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
</intent-filter>
</receiver>
MY_PACKAGE_REPLACED es el que disparan las actualizaciones de la aplicación — tu código está fresco, pero tus alarmas han desaparecido. Vuelve a programar ahí también.
Los optimizadores de los fabricantes — los asesinos silenciosos
En 2026 el peor infractor que queda sigue siendo todavía la lista de «Aplicaciones en suspensión» de Samsung, con el autoarranque y el ahorro de batería de Xiaomi justo detrás. Dos defensas prácticas:
- Banner de diagnóstico dentro de la aplicación. Si no se han disparado notificaciones en 24 horas y el usuario tiene recordatorios activados, muestra un pequeño banner con un enlace profundo «Arreglar esto» a la página correcta de ajustes del fabricante. Stack Overflow y el proyecto Don’t Kill My App mantienen actualizados esos enlaces profundos; guárdalos en caché localmente.
- No pidas la lista blanca de batería con avidez. Asusta a los usuarios y Play lo ve con malos ojos. Resérvalo para el usuario que ya ha visto un recordatorio perdido.
El patrón que acabó funcionando
Tras tres iteraciones en Hydrame, la arquitectura que por fin se sintió fiable:
- Un único trabajo periódico de
WorkManagerse ejecuta cada noche. Lee la programación del usuario e inserta las alarmas exactas de las siguientes 24 horas. - Cada alarma exacta publica una notificación a través de un
BroadcastReceiver. El receiver es tonto — sin llamadas a la base de datos, sin red. - Los receivers de reinicio + cambio de zona horaria + actualización de la aplicación disparan manualmente ese mismo trabajo nocturno para recrear la programación.
- Una métrica de latido (marca de tiempo de la última notificación exitosa, almacenada en DataStore) alimenta el banner dentro de la aplicación de «tus recordatorios pueden ser eliminados por tu teléfono».
Es más cableado del que habría escrito si hubiera confiado en mi primer instinto («usa solo WorkManager»). Pero cada pieza existe porque algo se rompió silenciosamente sin ella.
Probar en teléfonos reales, otra vez
Repetiré la misma frase de cada artículo de Android que escribo: no puedes probar recordatorios fiables solo en el emulador. Consigue un Samsung barato, un Xiaomi barato y un Pixel. Programa un recordatorio para dentro de 30 minutos. Bloquea el teléfono y déjalo sobre el escritorio. Vuelve y comprueba qué se disparó y qué no. Ese único experimento vale más que cualquier artículo de blog.
Incluido este. Ve a probarlo.
// Lecturas relacionadas
Más del diario
Acciones de notificación en Android bien hechas: marcar una dosis como tomada sin abrir la app
Una guía práctica de los botones de acción de notificación en Android — PendingIntent, la restricción del trampolín de actividad, goAsync() y actualizar el estado de Room con seguridad.
Cómo construir una rutina de medicación a la que de verdad te apegues
Una guía práctica sobre la adherencia a la medicación: por qué la gente se salta dosis, cómo funciona realmente el momento de los recordatorios, y cómo construir una rutina que sobreviva a una mala semana.
Una rutina de hidratación que no depende de la fuerza de voluntad
La mayoría de los consejos sobre hidratación se reducen a 'bebe más agua' y confían en que la fuerza de voluntad haga el resto. Aquí una rutina construida en torno a señales y recordatorios suaves, no a la disciplina.