AlarmManager and Sleepy Android Apps

Bruce Cooper
Bruce Cooper
Share

In my first article explaining How to Code an Android Widget, we covered the steps required to create a basic widget that updates on a non-frequent basis and uses standard components. We couldn’t get it to update the widget on a frequent basis however.

In this article, I will show you how to add a periodically firing update to your Widget using an ‘AlarmManager’. I make the assumption that you have already read the introduction Android Widget Coding article, or that you know the basics of App Widget programming.

Frequently Updating Widgets

Android widgets update their contents on a timed basis, waking the phone up to do so, even if the screen is off. If the update frequency is set too high, this can lead to excess battery usage. As a result, the Android system limits the frequency of widget updates to once every half hour or greater.

That’s no good if you want a frequently updating widget, like a clock. What we need here is a method of updating the widget frequently while the screen is on and only when the screen is on. Luckily there is a way this can be done, using a ‘AlarmManager‘, which is a system service wherein your code registers events to go off at particular times, or at particular periods. This allows something to happen at a particular time, even if the application is not running at that time. Even better, when you specify the right type of timer, you can tell it not to deliver the event if the phone is asleep at the time it goes off. This makes it perfect for our cause.

As with Widget updates, alarms are delivered as Intents, just as everything is in Android. So, the first thing we have to do is configure our application to listen to a custom intent type that we will create specifically for our clock updates.

Open up AndroidManifest.xml and add a new intent-filter beneath the existing filter for APPWIDGET_UPDATE. Because we are using a custom intent, we get to name it ourselves, but it should be fully qualified (i.e. have a reversed domain name on the front) so that we can differentiate between our intents and everyone else’s on the system. In this circumstance, I have chosen com.eightbitcloud.example.widget.8BITCLOCK_WIDGET_UPDATE as the name.

<intent-filter>
    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    <intent-filter>
    <action android:name="com.eightbitcloud.example.widget.8BITCLOCK_WIDGET_UPDATE" />
</intent-filter>

Next we need to write some code in our ‘ProviderHandler’ to configure the ‘AlarmManager’ to send out our intent on a regular basis. Load up the ‘WidgetProvider’ that we created in the first tutorial, and add the following methods:

private PendingIntent createClockTickIntent(Context context) {
    Intent intent = new Intent(CLOCK_WIDGET_UPDATE);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    return pendingIntent;
}

@Override
public void onEnabled(Context context) {
        super.onEnabled(context);
        Log.d(LOG_TAG, "Widget Provider enabled.  Starting timer to update widget every second");
        AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.A
LARM_SERVICE);

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.add(Calendar.SECOND, 1);
        alarmManager.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), 1000
, createClockTickIntent(context));
}

@Override
public void onDisabled(Context context) {
        super.onDisabled(context);
        Log.d(LOG_TAG, "Widget Provider disabled. Turning off timer");
        AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.cancel(createClockTickIntent(context));
}

onEnabled() and onDisabled() are called respectively when the first widget is created of your type, and the last is deleted. We want to start sending out the regular ticks when the first widget is created, and we want to stop the ‘AlarmManager’ when the last widget is deleted. We simply call AlarmManager.setRepeating() to create the repeating alarm, and then call cancel() when we are done with it.

Note that when we create the alarm, we specify an Alarm Type of AlarmManager.RTC. This type of alarm is only generated while the phone is awake. This is the feature that we rely upon to deliver updates only when the is awake, and not when the device is asleep.

Now we’ve told Android that our application is interested in receiving these intents, and we’ve configured the ‘AlarmManager’ to send the intents out on a regular basis. All that is left is to write the code that will handle the intents when they arrive. Alter your ‘WidgetProvider‘ class to include the following code:

/**
 * Custom Intent name that is used by the 'AlarmManager' to tell us to update the 
clock once per second.
 */
public static String CLOCK_WIDGET_UPDATE = "com.eightbitcloud.example.widget.8BITCLOCK_WIDGET_UPDATE";

@Override    public void onReceive(Context context, Intent intent) {
    super.onReceive(context, intent);
    Log.d(LOG_TAG, "Received intent " + intent);
    if (CLOCK_WIDGET_UPDATE.equals(intent.getAction())) {
        Log.d(LOG_TAG, "Clock update");
        // Get the widget manager and ids for this widget provider, then call the shared
        // clock update method.
        ComponentName thisAppWidget = new ComponentName(context.getPackageName(), getClass().getName());
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
        int ids[] = appWidgetManager.getAppWidgetIds(thisAppWidget);
        for (int appWidgetID: ids) {
            updateAppWidget(context, appWidgetManager, appWidgetID);
        }
    }
}

This code listens for the intent, and updates the component contents when received. The actual code to update the widget is the same as it was in the previous article, but has been refactored into a method called updateAppWidget to avoid repeating ourselves. Have a look at the full code for the class for more information.

So there you have it. Using the ‘AlarmManager’ we can get the Android operating system to send our application an intent on a regular basis. In this example, we’ve used the technique to update a widget on a very frequent basis, but it can also be used to do periodic updates for other components in your application. As an example, I often see that people have background services running to update their network feeds on a periodic basis. This works, but the service sits there running all the time, taking up memory on the device. If they were to use an ‘AlarmManager’, they could simply wake up their service each time it needed to do an update.

I hope that this article has shown how easy the ‘AlarmManager’ is to use. As always, the code is available on GitHub at Widget Example. In a future article, we’ll look at how to introduce complex user interface components to widgets.

Frequently Asked Questions (FAQs) about AlarmManager and Sleepy Android Apps

How does AlarmManager work in Android?

AlarmManager is a class in Android that provides access to the system alarm services. It allows you to schedule your application to run at a specific time in the future. It works even if your app is not currently running or even if the device is asleep. AlarmManager holds a CPU wake lock that provides guarantee that the phone will not sleep until you have finished handling the broadcast.

What is the difference between setExact() and setExactAndAllowWhileIdle() in AlarmManager?

The setExact() method sets an alarm to occur at an exact time. However, starting from Android 6.0 (API level 23), the system may shift alarms in order to minimize wake ups and battery use. On the other hand, setExactAndAllowWhileIdle() method allows you to set an alarm that will be delivered at an exact time, even if the device is in Doze mode.

How can I cancel an alarm in AlarmManager?

To cancel an alarm, you need to create an identical PendingIntent to the one that was used to set the alarm, and then call the cancel() method on the AlarmManager, passing in the PendingIntent.

Can AlarmManager wake up a device from sleep?

Yes, AlarmManager can wake up a device from sleep. It holds a CPU wake lock that ensures the phone will not sleep until you have finished handling the broadcast.

How can I use AlarmManager to set repeating alarms?

To set a repeating alarm, you can use the setRepeating() method of AlarmManager. You need to specify the type of the alarm, the time to start the alarm, the interval at which the alarm should repeat, and a PendingIntent that will be broadcasted when the alarm goes off.

What is the difference between RTC and ELAPSED_REALTIME in AlarmManager?

RTC (Real Time Clock) and ELAPSED_REALTIME are types of alarms in AlarmManager. RTC wakes up the device at a precise time, while ELAPSED_REALTIME wakes up the device after a certain amount of time has elapsed since boot.

How can I ensure that my alarm runs at the exact time in different Android versions?

To ensure that your alarm runs at the exact time across different Android versions, you can use the setExact() method for versions below Android 6.0, and the setExactAndAllowWhileIdle() method for Android 6.0 and above.

Can AlarmManager run when the app is closed?

Yes, AlarmManager can run even when the app is closed. It allows you to schedule your application to run at a specific time in the future, regardless of whether the app is currently running or not.

How can I use AlarmManager with BroadcastReceiver?

To use AlarmManager with BroadcastReceiver, you need to create a PendingIntent that will be broadcasted when the alarm goes off. In your BroadcastReceiver, you can then handle the broadcast and perform any actions you need.

What is the impact of AlarmManager on battery life?

While AlarmManager is a powerful tool, it can have a significant impact on battery life if not used carefully. Alarms that wake up the device frequently can drain battery quickly. Therefore, it’s important to use methods like setInexactRepeating() or setAndAllowWhileIdle() to minimize battery use.