August 21, 2014

Wake Up with the Alarm

alarmA popular issue in Android-dom is “where is cron?” The reference is to the cron utility in Linux, which runs commands on a pre-determined schedule, such as every hour. While Android does not have cron per se, it does have a similar facility, in the form of AlarmManager. AlarmManager differs from cron in several ways, most importantly:

  1. Whereas cron executes a command, AlarmManager broadcasts an Intent. So, to be woken up by AlarmManager, you need to provide it with an Intent that will cause the right bit of your code to get control when the alarm time rolls around.
  2. The receiver of this Intent must be a BroadcastReceiver registered in your manifest to be filtering on the desired Intent. The cron utility, on the other hand, lets  you run arbitrary commands.
  3. You infrequently have to worry about a PC falling asleep while executing a cron job. On the other hand, this happens a lot with AlarmManager.

You may, if you wish, have AlarmManager wake up the device when an alarm time arrives. In that case, AlarmManager will ensure that your BroadcastReceiver is awake long enough for it to process onReceive()…and nothing more. Since you are not supposed to do a lot of work in the BroadcastReceiver and cannot spawn threads from it, your most likely course of action will be to pass control from the BroadcastReceiver to a Service that is managing a background thread. The work can get done in the thread; the BroadcastReceiver’s thread can return control to Android quickly.

The catch is that the work on the background thread may take too long. AlarmManager has no way of knowing your work is taking too long, and it might allow the device to fall back asleep before your work wraps up.

To combat this, you need to hold a WakeLock.

A WakeLock, obtained from the PowerManager system service, allows you to tell Android to keep the CPU running for a period of time. You want to hold a WakeLock long enough to get your work done and short enough such that you do not use a lot of power. As importantly, you need to make sure you acquire your WakeLock in your BroadcastReceiver, while the CPU is guaranteed by AlarmManager to be still running. The Service is then responsible for releasing the WakeLock when it is done with the background work.

You may also need to hold a WakeLock in other scenarios, particularly if you are playing back media. Android does not keep the device awake and running just because media is being played — you may want to offer your users a configuration option to keep the device awake during playback, and if they select that option, hold an appropriate WakeLock while you are playing the video clip or whatnot.

This is terribly complicated, and over time, we will develop patterns and frameworks that will help with this sort of thing.

To learn more about AlarmManager and WakeLocks:

  • There are many threads on [android-developers] about this, including this one that triggered this blog post
  • You can download some sample code off of this page that demonstrates this technique (scroll down to the Source Code link; the sample code is in SystemServices/Alarm)