Cómo programar alarmas

Las alarmas (basadas en la clase AlarmManager) te brindan una forma de realizar operaciones programadas fuera del ciclo de vida de tu app. Por ejemplo, puedes usar una alarma para iniciar una operación de larga duración, como iniciar un servicio una vez al día para descargar el pronóstico del tiempo.

Las alarmas tienen las siguientes características:

  • Te permiten activar intents en horas o intervalos determinados.

  • Puedes usarlos junto con receptores de emisión para programar trabajos o WorkRequests para realizar otras operaciones.

  • Funcionan fuera de tu aplicación, por lo que puedes usarlas para activar eventos o acciones incluso cuando la app no se está ejecutando y el dispositivo en sí está suspendido.

  • Te ayudan a minimizar los requisitos de recursos de tu app. Puedes programar operaciones sin depender de temporizadores o ejecutar servicios de forma continua.

Cómo establecer una alarma inexacta

Cuando una app establece una alarma inexacta, el sistema entrega la alarma en algún momento en el futuro. Las alarmas inexactas brindan algunas garantías con respecto a la hora de la entrega de la alarma, a la vez que respetan las restricciones de ahorro de batería, como Descanso.

Los desarrolladores pueden aprovechar las siguientes garantías de la API para personalizar los tiempos de entrega de alarmas inexactas.

Cómo enviar una alarma después de una hora específica

Si tu app llama a set(), setInexactRepeating() o setAndAllowWhileIdle(), la alarma nunca se activa antes de la hora de activación proporcionada.

En Android 12 (nivel de API 31) y versiones posteriores, el sistema invoca la alarma dentro de una hora del tiempo de activación proporcionado, a menos que haya restricciones de ahorro de batería en efecto, como el ahorro de batería o Descanso.

Cómo enviar una alarma durante un período

Si tu app llama a setWindow(), la alarma nunca se activa antes de la hora de activación proporcionada. A menos que haya restricciones de ahorro de batería vigentes, la alarma se entrega dentro del período especificado, a partir de la hora de activación determinada.

Si tu app está orientada a Android 12 o versiones posteriores, el sistema puede retrasar la invocación de una alarma inexacta con ventana de tiempo por al menos 10 minutos. Por esta razón, los valores del parámetro windowLengthMillis en 600000 se recortan a 600000.

Cómo enviar una alarma repetitiva a intervalos aproximadamente regulares

Si tu app llama a setInexactRepeating(), el sistema invoca varias alarmas:

  1. La primera alarma suena dentro del período especificado, a partir de la hora de activación determinada.
  2. Por lo general, las alarmas posteriores suenan después de que transcurre el período especificado. El tiempo entre dos invocaciones consecutivas de la alarma puede variar.

Cómo establecer una alarma exacta

El sistema invoca una alarma exacta en un momento preciso del futuro.

La mayoría de las apps pueden programar tareas y eventos con alarmas inexactas para completar varios casos de uso comunes. Si la funcionalidad principal de tu app depende de una alarma con una hora precisa, como una app de alarma o de calendario, está bien usar una alarma exacta.

Casos de uso que podrían no exigir alarmas exactas

En la siguiente lista, se muestran los flujos de trabajo comunes que pueden no requerir una alarma exacta:

Programa operaciones de sincronización durante el ciclo de vida de tu app
La clase Handler incluye varios métodos útiles para controlar las operaciones de tiempo, como realizar algunas tareas cada n segundos mientras tu app está activa: postAtTime() y postDelayed(). Ten en cuenta que estas APIs se basan en el tiempo de actividad del sistema y no en el tiempo real.
Tarea en segundo plano programada, por ejemplo, actualizar la app y subir registros
WorkManager proporciona una forma de programar tareas periódicas sujetas a horarios específicos. Puedes proporcionar un intervalo de repetición y flexInterval (mínimo de 15 minutos) para definir el tiempo de ejecución detallado de la tarea.
Acción especificada por el usuario que debe ocurrir después de un tiempo específico (incluso si el sistema está inactivo)
Usa una alarma inexacta. Específicamente, llama a setAndAllowWhileIdle().
Acción especificada por el usuario que debe ocurrir después de un tiempo específico
Usa una alarma inexacta. Específicamente, llama a set().
Acción especificada por el usuario que puede ocurrir dentro de un período específico
Usa una alarma inexacta. Específicamente, llama a setWindow(). Ten en cuenta que, si tu app se orienta a Android 12 o versiones posteriores, la duración mínima permitida del período es de 10 minutos.

Formas de establecer una alarma exacta

Tu app puede establecer alarmas exactas con uno de los siguientes métodos. Estos métodos se ordenan de modo que los que están más cerca de la parte inferior de la lista ejecuten tareas más urgentes, pero demanden más recursos del sistema.

setExact()

Invoca una alarma en un momento casi preciso en el futuro, siempre que no haya otras medidas para ahorrar batería en efecto.

Usa este método para configurar alarmas exactas, a menos que el trabajo de tu app sea de importancia crítica para el usuario.

setExactAndAllowWhileIdle()

Invocar una alarma casi en un momento preciso en el futuro, incluso si hay medidas de ahorro de batería vigentes

setAlarmClock()

Invoca una alarma en un momento preciso en el futuro. Debido a que estas alarmas son muy visibles para los usuarios, el sistema nunca ajusta su tiempo de entrega. El sistema identifica estas alarmas como las más críticas y abandona los modos de bajo consumo si es necesario para entregarlas.

Consumo de recursos del sistema

Cuando el sistema activa las alarmas exactas que establece tu app, el dispositivo consume una gran cantidad de recursos, como la duración de batería, en especial si está en modo de ahorro de energía. Además, el sistema no puede agrupar en lotes estas solicitudes con facilidad para usar los recursos de manera más eficiente.

Se recomienda que crees una alarma inexacta siempre que sea posible. Para realizar trabajos más largos, programarlos con WorkManager o JobScheduler desde el BroadcastReceiver de tu alarma. Para realizar trabajos mientras el dispositivo está en modo Descanso, crea una alarma inexacta con setAndAllowWhileIdle() y comienza a trabajar a partir de la alarma.

Declara el permiso adecuado de alarmas exactas

Si tu app se orienta a Android 12 o versiones posteriores, debes obtener el acceso especial de apps a "Alarmas y recordatorios". Para ello, declara el permiso SCHEDULE_EXACT_ALARM en el archivo de manifiesto de tu app, como se muestra en el siguiente fragmento de código:

<manifest ...>
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
    <application ...>
        ...
    </application>
</manifest>

Si la app se orienta a Android 13 (nivel de API 33) o versiones posteriores, tienes la opción de declarar los permisos SCHEDULE_EXACT_ALARM o USE_EXACT_ALARM.

<manifest ...>
    <uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
    <application ...>
        ...
    </application>
</manifest>

Si bien los permisos SCHEDULE_EXACT_ALARM y USE_EXACT_ALARM indican las mismas capacidades, se otorgan de manera diferente y admiten distintos casos de uso. La app debe usar alarmas exactas y declarar el permiso SCHEDULE_EXACT_ALARM o USE_EXACT_ALARM solo si una función para el usuario requiere acciones sincronizadas con precisión.

USE_EXACT_ALARM

SCHEDULE_EXACT_ALARM

  • Otorgado por el usuario
  • Conjunto más amplio de casos de uso
  • Las apps deben confirmar que no se revocó el permiso

El permiso SCHEDULE_EXACT_ALARM no se otorga de forma previa a las instalaciones nuevas de apps que se orientan a Android 13 (nivel de API 33) y versiones posteriores. Si un usuario transfiere datos de la app a un dispositivo que ejecuta Android 14 mediante una operación de copia de seguridad y restablecimiento, se rechazará el permiso SCHEDULE_EXACT_ALARM en el dispositivo nuevo. Sin embargo, si una app existente ya tiene este permiso, se otorgará de forma previa cuando el dispositivo se actualice a Android 14.

Nota: Si la alarma exacta se configura con un objeto OnAlarmListener, como con la API de setExact, no se requiere el permiso SCHEDULE_EXACT_ALARM.

Cómo usar el permiso SCHEDULE_EXACT_ALARM

A diferencia de USE_EXACT_ALARM, el usuario debe otorgar el permiso SCHEDULE_EXACT_ALARM. Tanto el usuario como el sistema pueden revocar el permiso SCHEDULE_EXACT_ALARM.

Para verificar si se otorgó el permiso a tu app, llama a canScheduleExactAlarms() antes de intentar configurar una alarma exacta. Cuando se revoca el permiso SCHEDULE_EXACT_ALARM para tu app, esta se detiene y se cancelan todas las alarmas exactas futuras. Esto también significa que el valor que muestra canScheduleExactAlarms() permanece válido durante todo el ciclo de vida de tu app.

Cuando se le otorga a la app el permiso SCHEDULE_EXACT_ALARMS, el sistema le envía la emisión ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED. Tu app debe implementar un receptor de emisión que haga lo siguiente:

  1. Confirma que la app todavía tenga el acceso especial de apps. Para ello, llama a canScheduleExactAlarms(). Esta verificación protege tu app del caso en el que el usuario le otorga el permiso y, luego, lo revoca casi de inmediato.
  2. Reprograma cualquier alarma exacta que necesite la app, en función de su estado actual. Esta lógica debería ser similar a lo que hace la app cuando recibe la emisión ACTION_BOOT_COMPLETED.

Solicita a los usuarios que otorguen el permiso SCHEDULE_EXACT_ALARM

La opción se denomina &quot;Permitir configuración de alarmas y recordatorios&quot;
Figura 1: Página de acceso especial a "Alarmas y recordatorios" en la configuración del sistema, en la que los usuarios pueden permitir que la app establezca alarmas exactas.

Si es necesario, puedes enviar a los usuarios a la pantalla Alarmas y recordatorios en la configuración del sistema, como se muestra en la Figura 1. Para ello, completa los siguientes pasos:

  1. En la IU de la app, explícale al usuario por qué la app debe programar alarmas exactas.
  2. Invoca un intent que incluya la acción de intent ACTION_REQUEST_SCHEDULE_EXACT_ALARM.

Cómo establecer una alarma repetitiva

Las alarmas repetidas permiten que el sistema notifique a tu app en un programa recurrente.

Una alarma mal diseñada puede agotar la batería y sobrecargar los servidores. Por este motivo, en Android 4.4 (nivel de API 19) y versiones posteriores, todas las alarmas repetitivas son alarmas inexactas.

Una alarma repetitiva tiene las siguientes características:

  • Un tipo de alarma. (Para obtener más información, consulta Cómo elegir un tipo de alarma)

  • Una hora de activación Si la hora de activación que especificas es en el pasado, la alarma se activa de inmediato.

  • El intervalo de la alarma Por ejemplo, una vez al día, cada hora o cada 5 minutos.

  • Un intent pendiente que se activa cuando se activa la alarma Cuando configuras una segunda alarma que usa el mismo intent pendiente, esta reemplaza a la alarma original.

Para cancelar un PendingIntent(), pasa FLAG_NO_CREATE a PendingIntent.getService() para obtener una instancia del intent (si existe). AlarmManager.cancel()

Kotlin

val alarmManager =
    context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
val pendingIntent =
    PendingIntent.getService(context, requestId, intent,
                                PendingIntent.FLAG_NO_CREATE)
if (pendingIntent != null && alarmManager != null) {
  alarmManager.cancel(pendingIntent)
}

Java

AlarmManager alarmManager =
    (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent =
    PendingIntent.getService(context, requestId, intent,
                                PendingIntent.FLAG_NO_CREATE);
if (pendingIntent != null && alarmManager != null) {
  alarmManager.cancel(pendingIntent);
}

Cómo elegir un tipo de alarma

Una de las primeras consideraciones al usar una alarma repetitiva es el tipo.

Hay dos tipos de relojes generales para las alarmas: "tiempo real transcurrido" y "reloj en tiempo real" (RTC). El tiempo real transcurrido usa el "tiempo desde que se inició el sistema" como referencia, y el reloj en tiempo real usa la hora UTC (reloj de pared). Por lo tanto, el tiempo real transcurrido es apropiado para establecer una alarma basada en el paso del tiempo (por ejemplo, una alarma que se activa cada 30 segundos), ya que esta no se ve afectada por la zona horaria ni por la configuración regional. El tipo de reloj en tiempo real funciona mejor para las alarmas que dependen de la configuración regional actual.

Ambos tipos tienen una versión de "activación", que indica que se debe activar la CPU del dispositivo si la pantalla está apagada. De esta manera, se garantiza que la alarma se active a la hora programada. Esto es útil si tu app tiene una dependencia de tiempo. Por ejemplo, si tiene un período limitado para realizar una operación específica. Si no usas la versión de activación de tu tipo de alarma, entonces todas las alarmas repetitivas se activarán cuando vuelva a activarse el dispositivo.

Si solo necesitas que tu alarma se active en un intervalo determinado (por ejemplo, cada media hora), usa uno de los tipos de tiempo real transcurrido. Por lo general, esta es la mejor opción.

Si necesitas que tu alarma se active en un momento determinado del día, entonces elige uno de los tipos de reloj en tiempo real basado en un reloj. Sin embargo, ten en cuenta que este enfoque puede tener algunas desventajas. Es posible que la app no se traduzca bien a otras configuraciones regionales y, si el usuario cambia la configuración de hora del dispositivo, podría provocar un comportamiento inesperado en la app. El uso de un tipo de alarma de reloj en tiempo real tampoco se ajusta bien, como se mencionó anteriormente. Te recomendamos que uses una alarma de "tiempo real transcurrido" si es posible.

A continuación, se muestra una lista de los tipos:

  • ELAPSED_REALTIME: Activa el intent pendiente en función de la cantidad de tiempo que transcurrió desde que se inició el dispositivo, pero no activa el dispositivo. El tiempo transcurrido incluye cualquier período en el que el dispositivo estuvo inactivo.

  • ELAPSED_REALTIME_WAKEUP: Despierta el dispositivo y activa el intent pendiente después de que transcurrió la cantidad de tiempo especificada desde que se inició el dispositivo.

  • RTC: Activa el intent pendiente a la hora especificada, pero no activa el dispositivo.

  • RTC_WAKEUP: Activa el dispositivo para activar el intent pendiente en el momento especificado.

Ejemplos de alarmas de tiempo real transcurrido

Estos son algunos ejemplos del uso de ELAPSED_REALTIME_WAKEUP.

Activa el dispositivo para activar la alarma en 30 minutos, y cada 30 minutos después de eso:

Kotlin

// Hopefully your alarm will have a lower frequency than this!
alarmMgr?.setInexactRepeating(
        AlarmManager.ELAPSED_REALTIME_WAKEUP,
        SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_HALF_HOUR,
        AlarmManager.INTERVAL_HALF_HOUR,
        alarmIntent
)

Java

// Hopefully your alarm will have a lower frequency than this!
alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
        SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_HALF_HOUR,
        AlarmManager.INTERVAL_HALF_HOUR, alarmIntent);

Activa el dispositivo para activar una única alarma (no repetitiva) en un minuto:

Kotlin

private var alarmMgr: AlarmManager? = null
private lateinit var alarmIntent: PendingIntent
...
alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmIntent = Intent(context, AlarmReceiver::class.java).let { intent ->
    PendingIntent.getBroadcast(context, 0, intent, 0)
}

alarmMgr?.set(
        AlarmManager.ELAPSED_REALTIME_WAKEUP,
        SystemClock.elapsedRealtime() + 60 * 1000,
        alarmIntent
)

Java

private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
...
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
        SystemClock.elapsedRealtime() +
        60 * 1000, alarmIntent);

Ejemplos de alarmas de reloj en tiempo real

A continuación, se muestran algunos ejemplos del uso de RTC_WAKEUP.

Activa el dispositivo para activar la alarma aproximadamente a las 2:00 p.m. y que se repita una vez al día a la misma hora:

Kotlin

// Set the alarm to start at approximately 2:00 p.m.
val calendar: Calendar = Calendar.getInstance().apply {
    timeInMillis = System.currentTimeMillis()
    set(Calendar.HOUR_OF_DAY, 14)
}

// With setInexactRepeating(), you have to use one of the AlarmManager interval
// constants--in this case, AlarmManager.INTERVAL_DAY.
alarmMgr?.setInexactRepeating(
        AlarmManager.RTC_WAKEUP,
        calendar.timeInMillis,
        AlarmManager.INTERVAL_DAY,
        alarmIntent
)

Java

// Set the alarm to start at approximately 2:00 p.m.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 14);

// With setInexactRepeating(), you have to use one of the AlarmManager interval
// constants--in this case, AlarmManager.INTERVAL_DAY.
alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
        AlarmManager.INTERVAL_DAY, alarmIntent);

Activa el dispositivo para activar la alarma precisamente a las 8:30 a.m. y, desde entonces, cada 20 minutos:

Kotlin

private var alarmMgr: AlarmManager? = null
private lateinit var alarmIntent: PendingIntent
...
alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmIntent = Intent(context, AlarmReceiver::class.java).let { intent ->
    PendingIntent.getBroadcast(context, 0, intent, 0)
}

// Set the alarm to start at 8:30 a.m.
val calendar: Calendar = Calendar.getInstance().apply {
    timeInMillis = System.currentTimeMillis()
    set(Calendar.HOUR_OF_DAY, 8)
    set(Calendar.MINUTE, 30)
}

// setRepeating() lets you specify a precise custom interval--in this case,
// 20 minutes.
alarmMgr?.setRepeating(
        AlarmManager.RTC_WAKEUP,
        calendar.timeInMillis,
        1000 * 60 * 20,
        alarmIntent
)

Java

private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
...
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

// Set the alarm to start at 8:30 a.m.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 30);

// setRepeating() lets you specify a precise custom interval--in this case,
// 20 minutes.
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
        1000 * 60 * 20, alarmIntent);

Cómo decidir qué tan precisa debe ser la alarma

Como se describió anteriormente, seleccionar el tipo de alarma suele ser el primer paso para crear una. Otra distinción es qué tan precisa debe ser. Para la mayoría de las apps, setInexactRepeating() es la opción correcta. Cuando usas este método, Android sincroniza varias alarmas repetitivas que no son exactas y las activa al mismo tiempo. De esta manera, se reduce el consumo de batería.

Si es posible, evita usar alarmas exactas. Sin embargo, en el caso de una app particular que tenga requisitos de tiempo rígidos, puedes establecer una alarma exacta llamando a setRepeating().

Con setInexactRepeating(), no puedes especificar un intervalo personalizado como lo haces con setRepeating(). Debes usar una de las constantes de intervalo, como INTERVAL_FIFTEEN_MINUTES, INTERVAL_DAY, etcétera. Consulta AlarmManager para obtener la lista completa.

Cancela una alarma

Según tu app, es posible que quieras incluir la capacidad para cancelar la alarma. Para cancelar una alarma, llama a cancel() en el Administrador de alarmas y pasa el PendingIntent que ya no quieras activar. Por ejemplo:

Kotlin

// If the alarm has been set, cancel it.
alarmMgr?.cancel(alarmIntent)

Java

// If the alarm has been set, cancel it.
if (alarmMgr!= null) {
    alarmMgr.cancel(alarmIntent);
}

Cómo iniciar una alarma cuando se reinicia el dispositivo

De manera predeterminada, todas las alarmas se cancelan cuando se apaga un dispositivo. Para evitar que esto suceda, puedes diseñar tu app para que reinicie automáticamente una alarma repetitiva en caso de que el usuario reinicie el dispositivo. De esta manera, se garantiza que AlarmManager continúe realizando la tarea sin que el usuario deba reiniciar la alarma de forma manual.

A continuación, se indican los pasos que debes seguir:

  1. Configura el permiso RECEIVE_BOOT_COMPLETED en el manifiesto de tu app. De esta manera, tu app podrá recibir el ACTION_BOOT_COMPLETED que se transmite una vez que el sistema finaliza el inicio (solo funciona si el usuario ya inició la app al menos una vez):

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
  2. Implementa un BroadcastReceiver para recibir la transmisión:

    Kotlin

    class SampleBootReceiver : BroadcastReceiver() {
    
        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == "android.intent.action.BOOT_COMPLETED") {
                // Set the alarm here.
            }
        }
    }

    Java

    public class SampleBootReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
                // Set the alarm here.
            }
        }
    }
  3. Agrega el receptor al archivo de manifiesto de tu app con un filtro de intents que filtre la acción ACTION_BOOT_COMPLETED:

    <receiver android:name=".SampleBootReceiver"
            android:enabled="false">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"></action>
        </intent-filter>
    </receiver>

    Ten en cuenta que, en el manifiesto, el receptor de inicio está configurado en android:enabled="false". lo que significa que no se lo llamará a menos que la app lo habilite explícitamente. De esta manera, se evita que se llame innecesariamente al receptor de inicio. Para habilitar un receptor (por ejemplo, si el usuario establece una alarma), haz lo siguiente:

    Kotlin

    val receiver = ComponentName(context, SampleBootReceiver::class.java)
    
    context.packageManager.setComponentEnabledSetting(
            receiver,
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            PackageManager.DONT_KILL_APP
    )

    Java

    ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
    PackageManager pm = context.getPackageManager();
    
    pm.setComponentEnabledSetting(receiver,
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            PackageManager.DONT_KILL_APP);

    Una vez que habilites el receptor de esta manera, permanecerá habilitado, incluso si el usuario reinicia el dispositivo. En otras palabras, habilitar el receptor de manera programática anula la configuración del manifiesto, incluso después de un reinicio. El receptor permanecerá habilitado hasta que tu app lo inhabilite. Puedes inhabilitar un receptor (por ejemplo, si el usuario cancela una alarma) de la siguiente manera:

    Kotlin

    val receiver = ComponentName(context, SampleBootReceiver::class.java)
    
    context.packageManager.setComponentEnabledSetting(
            receiver,
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP
    )

    Java

    ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
    PackageManager pm = context.getPackageManager();
    
    pm.setComponentEnabledSetting(receiver,
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP);

Invocar alarmas mientras el dispositivo está en modo Descanso

Los dispositivos que ejecutan Android 6.0 (nivel de API 23) admiten el modo Descanso, que ayuda a extender la duración de la batería del dispositivo. Las alarmas no se activan cuando el dispositivo está en el modo Descanso. Cualquier alarma programada se pospone hasta que el dispositivo salga de este modo. Si necesitas completar el trabajo incluso cuando el dispositivo está inactivo, hay varias opciones disponibles:

  • Establece una alarma exacta.

  • Usa la API de WorkManager, que se compiló para realizar trabajos en segundo plano. Puedes indicar que el sistema debe acelerar tu trabajo para que se termine lo antes posible. Para obtener más información, consulta Cómo programar tareas con WorkManager.

Prácticas recomendadas

Cada decisión que tomes a la hora de diseñar tu alarma repetitiva puede tener consecuencias en la manera en que tu app usa (o abusa) de los recursos del sistema. Por ejemplo, imagina una app popular que se sincroniza con un servidor. Si la operación de sincronización se basa en la hora del reloj y cada instancia de la app se sincroniza a las 11:00 p.m., la carga en el servidor puede generar una latencia alta o incluso "la denegación del servicio". Sigue estas prácticas recomendadas para usar las alarmas:

  • Agrega aleatorización (jitter) en cualquier solicitud de red que se active a causa de una alarma repetitiva:

    • Realiza las tareas locales cuando se active la alarma. Con "tareas locales" nos referimos a cualquier tarea que no interactúe con un servidor ni requiera datos de uno.

    • Al mismo tiempo, programa la alarma que contenga las solicitudes de red para que se active en algún momento aleatorio.

  • Mantén la frecuencia de la alarma al mínimo.

  • No actives el dispositivo innecesariamente (este comportamiento se determina por el tipo de alarma, como se describe en Cómo elegir un tipo de alarma).

  • No hagas que el tiempo de activación de tu alarma sea más preciso de lo necesario.

    Usa setInexactRepeating() en lugar de setRepeating(). Cuando usas setInexactRepeating(), Android sincroniza las alarmas repetitivas de varias apps y las activa al mismo tiempo. De esta manera, se reduce la cantidad total de veces que el sistema debe activar el dispositivo, lo que minimiza el consumo de la batería. A partir de Android 4.4 (nivel de API 19), todas las alarmas repetitivas son una alarma inexacta. Ten en cuenta que, si bien setInexactRepeating() es una mejora con respecto a setRepeating(), de igual manera puede sobrecargar el servidor si cada instancia de una app interactúa con él al mismo tiempo. Por lo tanto, en el caso de las solicitudes de red, agrega aleatorización a tus alarmas, como se explicó anteriormente.

  • Si es posible, evita basar tu alarma en la hora del reloj.

    Las alarmas repetitivas que se basan en una hora de activación precisa no se escalan bien. Usa ELAPSED_REALTIME si puedes. En la siguiente sección, se describen con más detalle los diferentes tipos de alarmas.