Yesterdays #AndroidDev #Protip explains how to use WakefulBroadcastReceiver
utility class and what problem does it solve, but it doesn’t mention a case when using it or manually acquiring WakeLock
is essential – using the AlarmManager
.
If you’re not familiar with AlarmManager
’s API, here is tl;dr of the docs: it allows you to specify the PendingIntent
that should be fired at some point, even if your application is in background. The common use cases for using AlarmManager
is for example showing a Notification
at the specified time or sending some kind of heartbeat to your backend. In both cases, your code performs potentially long running operation (in case of showing notification you might need some content from your local database), so you don’t want to run it in the UI thread. The first thing that comes to mind is to specify an IntentService
as a PendingIntent
target:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
This code won’t always work though. While it is guaranteed that the alarm will go off and the PendingIntent
will be sent, because we used a _WAKEUP
alarm type, the device is allowed to go back to sleep before the service is started.
It’s not explicitly documented, but both +Dianne Hackborn and +CommonsWare confirmed this. The workaround is to use PendingIntent.getBroadcast()
, because it is guaranteed that the BroadcastReceiver.onReceive()
will be always fully executed before the CPU goes to sleep. Inside that callback you have to acquire WakeLock
start your IntentService
and release the lock at the end of onHandleIntent()
method.
This is where the WakefulBroadcastReceiver
comes into play: its startWakefulService
and completeWakefulIntent
static methods encapsulate all the WakeLocks
juggling, allowing you to focus on your business logic.