Android apps often need to perform actions some time in the future. This can be “one shot” type operations or periodic tasks. For simple needs while an app’s Activity
or Service
is in the running state, this can be done using a few different mechanisms such as a CountDownTimer
or the old standby Timer
. If your app has actions to perform regardless of the state of your components, you need to harness the power of the Android alarm.
Android Alarm: What It Is
As the name implies, an Android Alarm is something which wakes your app at some point in time. But, it wields more power than just that: it can wake your app regardless of whether it is running or not and it can wake the entire Android device if it is asleep. So this isn’t just a way to wake up a paused thread in your running app. This is the software equivalent of an air-horn used to rouse your Android app out of bed to get moving and even get the whole device up and running if it is sleeping.
There are essentially two different types of alarm instances: one which wakes your app the next time the device is awakened and one which wakes both the device and your app. Let’s call these the “get up when you can” type and the “get up, the house is on fire” type. Ok, that was a bit much, let’s just call them “gentle” and “aggressive”. Either type can have their time specified by either the elapsed uptime of the Android device or via calendar time. They also can be setup to be periodic.
It’s worth noting before I go further that the use of alarms isn’t a necessity for all periodic or timed operations. For example, if your Android app is a turn based game which gives each user a certain period of time to submit a turn then using an alarm would be overkill. A simple CountDownTimer
or even a Handler
receiving a Message
object periodically to do the countdown will work (and are easier to deal with.) Alarms are meant to be used when you need your app and/or the device as a whole to be awakened to perform some action. Such as your app using a Service
which needs to fetch updated data from your back end server via a REST API.
Behind the Scenes
Before we talk about how to use an alarm, it’s very useful to understand the backing mechanism. You may think that it’s just setting a timer to fire and wake up your UI thread, case closed. But, that cannot be the case because your app may actually not be running at all and the device may be in a low power state. So, we need to go through some of the Android framework’s plumbing to get a glimpse of what is going on.
The high level diagram above depicts the components involved when setting an alarm within an app called MyApp. The app gets an instance of the AlarmManager
and sets an alarm using a PendingIntent
. More on usage and setting alarms is coming in the next section. The AlarmManager
is the app side interface to the backing AlarmManagerService
. It abstracts the details of the Binder
interface, used to communicate with the system process (system_server
) hosting the AlarmManagerService
. These two components manage the alarm(s) the app has set and will send the PendingIntent
correctly. This manager/service architecture is used throughout Android framework and is done for security and isolation purposes. The system_server
process is running with privileges which normal apps do not have. If you are unfamiliar with Android’s use of permissions, see this article for more details on app processes and user IDs. These extra permissions are what allows system_server
to access the underlying kernel alarm driver. The alarm driver is what manages setting alarms to wake up the device regardless of the sleep state of the SoC.
When the alarm is triggered the device is awakened (if asleep) and the AlarmManagerService
is notified of an alarm expiring. It will then send the PendingIntent
accordingly. This will cause the appropriate component within MyApp to be activated. If MyApp has not been started or its process is not cached, it will be started so the component can be activated.
Basic Usage
Now that we have the basic concept and players outlined, let’s take a quick look at how to actually use an Android alarm. The first step is to get an AlarmManager
object so we can manipulate our alarm. The AlarmManager
is an Android framework provided class which is not instantiated by apps but acquired. This means instead of calling “new” to create an instance of one, we ask our Context
to give us the object:
public class MyActivity extends Activity { ... private AlarmManager mAlarmMgr; ... public void onCreate(Bundle savedInstance) { ... mAlarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE); ... } ... }
Now to set an alarm, we need to create a PendingIntent
object. You can think of a PendingIntent
as a deferred Intent
to be sent by another application on your behalf. This is a convenient way to call you back when something occurs, like an Android alarm. When a PendingIntent
is created is it created for a specific component target (e.g. Activity
, Service
or BroadcastReceiver
) and you provide the “real” Intent
payload to be delivered. This is accomplished using the static methods: getActivity()
, getService()
or getBroadcast()
. As an example, let’s create a PendingIntent
for our MyActivity
using the component name.
Intent alarmIntent = new Intent(context, MyActivity.class); PendingIntent pend = PendingIntent.getActivity(context, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
So this all sounds great, but there is a word of warning with regards to PendingIntents
and their usage. When the PendingIntent
is actually sent, it is done with the same application identity and permissions as the app which created it. This means the other app is sending something as if it came from your app. For that reason, it’s best to always specify the specific target of the Intent
so it does not inadvertently get sent to another app as if it came from yours. The sending app (process) can also alter certain fields within the Intent
, so it is a best practice to always validate your Intent
payload when it is received.
Now that we have our PendingIntent
and the AlarmManager
, we can set our alarm so our Activity
is triggered when the alarm has expired. To do this, we need to figure out when we want our alarm to go off and whether it should wake up the device or just be delivered the next time the device is awakened. Remember, we have two different ways of specifying time for our alarms: elapsed time or calendar (RTC) time. So our options are ELAPSED_REALTIME
, ELAPSED_REALTIME_WAKEUP
, RTC
or RTC_WAKEUP
. The _WAKEUP
variants are our “aggressive” alarms where we want the device to come out of low power to call our app back. For our sample app, let’s set this up in a custom BroadcastReceiver
and have it trigger our Activity
about 30 seconds after the device is booted.
public class MyBootReceiver extends BroadcastReceiver { public void onReceive(Context, context, Intent intent) { ... AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); long wakeTime = SystemClock.elapsedRealtime() + 30000; alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, wakeTime, pend); } }
Now when our device boots and the BOOT_COMPLETED
broadcast is sent, our app’s process will be started and our receiver will set an alarm to trigger our Activity
to be launched about 30 seconds later. Note that on Android 3.1 devices or newer, you must first manually launch your app before the BOOT_COMPLETED
.
Something else in which to be aware: alarms do not persist across reboots or power downs. So while your alarm will wake up the system from a sleep state, it will not stick around if the device is power cycled. Your app would have to re-run and set the alarm again after the next boot.
Version Specific Behavior
Like many APIs within Android, the alarm API continues to evolve. However, if you simply look at the “added in API” info in the docs for the AlarmManager and don’t read the fine print you could miss something very important. Starting with API 19 (KitKat), the alarms are inexact by default. What this means is that the system takes your requested time and shifts it to be in-line with other alarm times which have been requested. So your alarm will expire “about” the same time as you requested. Why was this done? It’s all about the power savings. Android is very aggressive with regards to power savings and sleep state, so if numerous alarms are all being set and expiring it can create a lot of “thrashing” and unnecessary wake ups. With this behavior, the system will make it so as many alarms as possible are expiring at the same time to cause the wake up. That being said, there are still ways to adjust this, if needed.
First, the system will still treat your timers are “exact” if the target API is lower than 19. This is the legacy behavior and it will honor it. Obviously, if you are already using the setInexactRepeating()
method then this doesn’t apply.
Second, there were two new APIs added to explicitly set a timer to be exact or to tell the system of an acceptable window of time in which your alarm can expire. These APIs are setExact()
and setWindow()
, respectively. If this is behavior you require and you are targeting API 19 or newer, definitely give the docs a read.
It’s also worth noting that repeating timers are always inexact in API 19 or newer. If you need exact timing, you have to use setExact()
or setWindow()
then manually re-set your timer rather than use the system provided repeating APIs.
Power Management Ties
Let’s wrap up with details about alarms and power management. Remember: our alarms are potentially waking the entire device so our app can be notified the alarm has expired and take some action. But, Android really wants to be sleeping whenever possible so we need to be cautious about how we use alarms and also handle potential conflicts with power management.
Android’s power management is handled both within the framework provided PowerManager
as well as the underlying Linux kernel. The Android Linux kernel uses something called wakelocks to determine when to go to sleep. Unlike traditional Linux workstation/server systems, the kernel is constructed so that when there is nothing actively going on (CPU reaches idle) it is going to power down as much as it can and go to sleep. Modern SoCs have outstanding support for selectively powering off sections of the silicon to save on power and can even sleep the CPU itself. The power savings are enormous by doing this, which ultimately increases our battery life. So rather than sleep being something an app (or the user) requests, Android will automatically drop to sleep state when it is idle. The wakelock mechanism allows drivers and apps to prevent this. While a wakelock is being held, the system is prevented from going to sleep.
As we’ve already seen, an Android alarm can be used to wake up the entire device if it was sleeping. But, what isn’t obvious is that there is a good chance the device may immediately go back to sleep unless we structure our alarm a certain way and also use our own wakelock. In my example above, the alarm was set with a PendingIntent
to start an Activity
. This works just fine as long as the device is already awake or there is something else to keep it awake once the alarm expires. One of the subtle things about the AlarmManager
is that it really expects you to create an alarm with a PendingIntent
for a BroadcastReceiver
rather than an Activity
or Service
. It will automatically hold a wakelock on your behalf until your receiver’s onReceive()
method has returned. What does this mean? It means that if your app has something to do outside of the receiver, you need to create and hold your own wakelock then release it later in your Activity
or Service
when you are done performing your specific action(s). It also means that if your alarm’s PendingIntent
is for an Activity
or Service
then there is a good chance that your alarm will expire and the device will sleep again before the Activity
or Service
gets created an run. A sample app with the above code revised using wakelocks is available in a GitHub repository. See the reference at the end of this post.
Recap
Performing periodic or timed actions can be done by any app using the Android alarm mechanism. It is an easy to use API, once you understand the backing mechanism. Alarms are tracked and handled by the AlarmManagerService
within the Android framework with the help of the alarm
driver within the Android Linux kernel. This allows not only the app requesting the alarm to be awakened, but also the device as a whole if it is in a low power state. We saw that alarms can be set by elapsed real time or calendar time and utilize a PendingIntent
to activate one of our app’s components (typically a BroadcastReceiver
.) Because the device may have been awakened, there are some power considerations to be made in your code by appropriately holding a wakelock when taking action based on your alarm so the device does not quickly go back to sleep.
What has been your experience with using an Android alarm? Have you run into any strange race conditions because of the power management handling, or perhaps not understood the new inexact behavior?
References
Sample app GitHub repository: https://github.com/hiq-larryschiefer/myalarmapp.git