8.2 Alarms
Contents
- Introduction
- Alarm types
- Alarm best practices
- Scheduling an alarm
- Checking for an existing alarm
- Canceling an alarm
- User-visible alarms ("alarm clocks")
- Related practical
- Learn more
You already know how to use broadcast receivers to make your app respond to system events even when your app isn't running. In this chapter, you learn how to use alarms to trigger tasks at specific times, whether or not your app is running when the alarms go off. Alarms can either be single use or repeating. For example, you can use a repeating alarm to schedule a download every day at the same time.
To create alarms, you use the AlarmManager
class. Alarms in Android have the following characteristics:
- Alarms let you send an
Intent
at set times or intervals. You can use alarms with broadcast receivers to start services and perform other operations. - Alarms operate outside your app. You can use them to trigger events or actions even when your app isn't running, and even if the device is asleep.
- When used correctly, alarms can help you minimize your app's resource requirements. For example, you can schedule operations without relying on timers or continuously running background services.
When not to use alarms:
- Don't use alarms for timing events such as ticks and timeouts, or for timed operations that are guaranteed to happen during the lifetime of your app. Instead, use the
Handler
class withTimer
andThread
. This approach gives Android better control over system resources than if you used alarms. - Don't use alarms for server sync operations. Instead, use
SyncAdapter
with Firebase Cloud Messaging. - You might not want to use alarms for tasks that can wait until conditions are favorable, such as when the device is connected to Wi-Fi and is charging. (These tasks include things like updating weather information or news stories.) For these tasks on devices running API 21 or higher, consider using
JobScheduler
, which you learn about in an upcoming lesson. - Don't use alarms for tasks that have to happen even if the device is idle. When a device is in Doze mode (idle), scheduled alarms are deferred until the device exits Doze. To guarantee that alarms execute even if the device is idle, use
setAndAllowWhileIdle()
orsetExactAndAllowWhileIdle()
. Another option is to use the newWorkManager
API, which is built to perform background work either once or periodically. For details, see Schedule tasks with WorkManager.
Alarm types
There are two general types of alarms in Android: elapsed real-time alarms and real-time clock (RTC) alarms, and both use PendingIntent
objects.
Elapsed real-time alarms
Elapsed real-time alarms use the time, in milliseconds, since the device was booted. Time zones don't affect elapsed real-time alarms, so these alarms work well for alarms based on the passage of time. For example, use an elapsed real-time alarm for an alarm that fires every half hour.
The AlarmManager
class provides two types of elapsed real-time alarm:
ELAPSED_REALTIME
: Fires aPendingIntent
based on the amount of time since the device was booted, but doesn't wake the device. The elapsed time includes any time during which the device was asleep. All repeating alarms fire when your device is next awake.ELAPSED_REALTIME_WAKEUP
: Fires thePendingIntent
after the specified length of time has elapsed since device boot. If the screen is off, this alarm wakes the device's CPU. If your app has a time dependency, for example if your app has a limited window during which to perform an operation, use this alarm instead ofELAPSED_REALTIME
.
Real-time clock (RTC) alarms
Real-time clock (RTC) alarms are clock-based alarms that use Coordinated Universal Time (UTC). Only choose an RTC alarm in these types of situations:
- You need your alarm to fire at a particular time of day.
- The alarm time is dependent on current locale.
Apps with clock-based alarms might not work well across locales, because they might fire at the wrong times. And if the user changes the device's time setting, it could cause unexpected behavior in your app.
The AlarmManager
class provides two types of RTC alarm:
RTC
: Fires the pending intent at the specified time but doesn't wake the device. All repeating alarms fire when your device is next awake.RTC_WAKEUP
: Fires the pending intent at the specified time. If the screen is off, this alarm wakes the device's CPU.
Alarm best practices
Alarms affect how your app uses (or abuses) system resources. For example, imagine a popular app that syncs with a server, and imagine that the sync operation is based on clock time. If every instance of the app connects to the server at the same time, the load on the server can delay responses or even create a "denial of service" condition.
To avoid this problem, add randomness (jitter) to network requests that trigger as a result of a repeating alarm. Here's one way to add randomness:
- Schedule an exact alarm that performs any local work. "Local work" is any task that doesn't contact a server over a network or require data from that server.
- Schedule a separate alarm that contains the network requests, and have this alarm fire after a random period of time. The component that receives the
PendingIntent
from the first alarm usually sets this second alarm. (You can also set this alarm at the same time as you set the first alarm.)
Other best practices:
- Keep your alarm frequency to a minimum.
- Don't wake the device unnecessarily.
- Use the least precise timing possible to allow the
AlarmManager
to be the most efficient it can be. For example, when you schedule a repeating alarm, usesetInexactRepeating()
instead ofsetRepeating()
. For details, see Scheduling a repeating alarm, below. - Avoid basing your alarm on clock time and use
ELAPSED_REALTIME
for repeating alarms whenever possible. Repeating alarms that are based on a precise trigger time don't scale well.
Scheduling an alarm
The AlarmManager
class gives you access to the Android system alarm services. AlarmManager
lets you broadcast an Intent
at a scheduled time, or after a specific interval.
To schedule an alarm:
- Call
getSystemService(ALARM_SERVICE)
to get an instance of theAlarmManager
class. - Use one of the
set...()
methods available inAlarmManager
(as described below). Which method you use depends on whether the alarm is elapsed real time, or RTC.
All the AlarmManager.set...()
methods include a type
argument and a PendingIntent
argument:
- Use the
type
argument to specify the alarm type. Alarms can be elapsed real-time alarms (ELAPSED_REALTIME
orELAPSED_REALTIME_WAKEUP
) or real-time clock alarms (RTC
orRTC_WAKEUP
). - A
PendingIntent
object, which is how you specify which task to perform at the given time.
Scheduling a single-use alarm
To schedule a single alarm, use one of the following methods on the AlarmManager
instance:
set()
: For devices running API 19+, this method schedules a single, inexactly timed alarm, meaning that the system shifts the alarm to minimize wakeups and battery use. For devices running lower API versions, this method schedules an exactly timed alarm.setWindow()
: For devices running API 19+, use this method to set a window of time during which the alarm should be triggered.setExact()
: For devices running API 19+, this method triggers the alarm at an exact time. Use this method only for alarms that must be delivered at an exact time, for example an alarm clock that rings at a requested time. Exact alarms reduce the OS's ability to minimize battery use, so don't use them unnecessarily.
Here's an example of using set()
to schedule a single-use alarm:
alarmMgr.set(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000*300,
alarmIntent);
In this example:
- The
type
isELAPSED_REALTIME
, so this is an elapsed real-time alarm. If the device is idle when the alarm is sent, the alarm does not wake the device. - The alarm is sent 5 minutes (300,000 milliseconds) after the method returns.
alarmIntent
is aPendingIntent
broadcast that contains the action to perform when the alarm is sent.
Handler
rather than an alarm.
Doze and App Standby
API 23+ devices sometimes enter Doze or App Standby mode to save power:
- Doze mode is triggered when a user leaves a device unplugged and stationary for a period, with the screen off. During short "maintenance windows," the system exits Doze to let apps complete deferred activities, including firing standard alarms, then returns to Doze. Doze mode ends when the user returns to their device.
- App Standby mode is triggered on idle apps that haven't been used recently. App Standby mode ends when the user returns to your app or plugs in the device.
Some alarms can wait for a maintenance window, or until the device comes out of Doze or App Standby mode. For these alarms, use the standard set()
and setExact()
methods to optimize battery life.
If you need an alarm that fires during Doze mode or App Standby mode without waiting for a maintenance window:
- For inexact alarms, use
setAndAllowWhileIdle()
and for exact alarms, usesetExactAndAllowWhileIdle()
, or - Set a user-visible alarm (API 21 and higher).
Scheduling a repeating alarm
You can also use the AlarmManager
to schedule repeating alarms, using one of the following methods:
setRepeating()
: On devices running Android versions lower than 4.4 (API Level 19), this method creates a repeating, exactly timed alarm. On devices running API 19 and higher,setRepeating()
behaves exactly likesetInexactRepeating()
.setInexactRepeating()
: This method creates a repeating, inexact alarm that allows for batching. When you usesetInexactRepeating()
, Android synchronizes repeating alarms from multiple apps and fires them at the same time. This reduces the total number of times the system must wake the device, thus reducing drain on the battery. As of API 19, all repeating alarms are inexact.
To decrease possible battery drain:
- Schedule repeating alarms to be as infrequent as possible.
- Use inexact timing, which allows the system to batch alarms from different apps together.
setInexactRepeating()
is an improvement over setRepeating()
, it can still overwhelm a server if every instance of an app reaches the server around the same time. Therefore, for network requests, add some randomness to your alarms, as described in Alarm best practices.
If you really need exact repeating alarms on API 19+, set a single-use alarm with setExact()
and set the next alarm once that alarm has triggered. This second alarm is set by whatever component receives the PendingIntent
—usually a service or a broadcast receiver.
Here's an example of using setInexactRepeating()
to schedule a repeating alarm:
alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(),
AlarmManager.INTERVAL_FIFTEEN_MINUTES,
alarmIntent);
In this example:
- The
type
isRTC_WAKEUP
, which means that this alarm is clock-based, and it wakes the device. - The first occurrence of the alarm is sent immediately, because
calendar.getTimeInMillis()
returns the current time as UTC milliseconds. After the first occurrence, the alarm is sent approximately every 15 minutes.
If the method were
setRepeating()
instead ofsetInexactRepeating()
, and if the device were running an API version lower than 19, the alarm would be sent exactly every 15 minutes.Possible values for this argument are
INTERVAL_DAY
,INTERVAL_FIFTEEN_MINUTES
,INTERVAL_HALF_DAY
,INTERVAL_HALF_HOUR
,INTERVAL_HOUR
.alarmIntent
is thePendingIntent
that contains the action to perform when the alarm is sent. ThisIntent
typically comes fromIntentSender.getBroadcast()
.
Checking for an existing alarm
It's often useful to check whether an alarm is already set. For example, if an alarm exists, you may want to disable the ability to set another alarm.
To check for an existing alarm:
Create a
PendingIntent
that contains the sameIntent
used to set the alarm, but this time, use theFLAG_NO_CREATE
flag.With
FLAG_NO_CREATE
, aPendingIntent
is only created if one with the sameIntent
exists. Otherwise, the request returnsnull
.Check whether the
PendingIntent
isnull
:If the
PendingIntent
isnull
, the alarm has not yet been set. If thePendingIntent
is notnull
, thePendingIntent
exists, meaning that the alarm has been set.
For example, the following code returns true
if the alarm contained in alarmIntent
already exists:
boolean alarmExists =
(PendingIntent.getBroadcast(this, 0,
alarmIntent,
PendingIntent.FLAG_NO_CREATE) != null);
Canceling an alarm
To cancel an alarm, use cancel()
and pass in the PendingIntent
. For example:
alarmManager.cancel(alarmIntent);
User-visible alarms ("alarm clocks")
For API 21+ devices, you can set a user-visible alarm clock by calling setAlarmClock()
. Apps can retrieve the next user-visible alarm clock that's set to go off by calling getNextAlarmClock()
.
Alarm clocks set with setAlarmClock()
work even when the device or app is idle, similar to the way setExactAndAllowWhileIdle()
works. Using setAlarmClock()
gets you as close to an exact-time wake-up call as possible.
Related practical
The related practical is in 8.2: The alarm manager.
Learn more
Android developer documentation:
Video: