Reader small image

You're reading from  Asynchronous Android Programming - Second Edition

Product typeBook
Published inJul 2016
Reading LevelBeginner
PublisherPackt
ISBN-139781785883248
Edition2nd Edition
Languages
Tools
Right arrow
Author (1)
Steve Liles
Steve Liles
author image
Steve Liles

Steve Liles is a self-confessed geek and has been an Android fan since the launch day of the G1. When he isn't at work building publishing systems and apps for newspapers and magazines, you'll find him tinkering with his own apps, building 3D printers, or playing RTS games. He is currently working with a start-up to build an advertising system that links the print and digital worlds using computer vision on Android and iOS devices.
Read more about Steve Liles

Right arrow

Chapter 6. Scheduling Work with AlarmManager

Maintaining the responsiveness of foreground apps has been our primary focus throughout this book, and we've explored numerous ways to shift work away from the main thread and run work in the background.

In all of our discussions so far, we wanted to get the work done as soon as possible, so although we moved it to a background thread, we still performed the work concurrently with ongoing main thread operations, such as updating the user interface and responding to user interaction.

In this chapter we will learn how to defer work with AlarmManager to run at some distant time in the future, initiating work without user intervention, and even waking up the device from an idle state if it is really necessary. Meanwhile, we will introduce you to some power saving features introduced with Android Marshmallow 6 and explain how to adapt your application to this new paradigm.

In this chapter we will cover the following topics:

  • Scheduling alarms with AlarmManager...

Introducing AlarmManager


In Chapter 2, Performing Work with Looper, Handler, and HandlerThread, we learned how to schedule work on a HandlerThread using postDelayed, postAtTime, sendMessageDelayed, and sendMessageAtTime. These mechanisms are fine for short-term scheduling of work while our application is running in the foreground.

However, if we want to schedule an operation to run at some point in the distant future, we'll run into problems. First, our application may be terminated before that time arrives, removing any chance of the Handler running those scheduled operations. Second, the device may be asleep, and with its CPU powered down it cannot run our scheduled tasks.

Note

The solution to this is to use an alternative scheduling approach, one that is designed to overcome these problems: AlarmManager.

android.app.AlarmManager is a class that has been available in the Android SDK since the first version, delivering an advanced API to fire off Intents in the future at one specific time...

Scheduling alarms with AlarmManager


As we said before, all the alarm operations are managed through the singleton object AlarmManager, an Android global system service that can be retrieved by any class with access to a Context instance. As an example, in an Activity we can get the AlarmManager from any member method by using the following code:

AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);

Once we have a reference to the AlarmManager, we can schedule an alarm to deliver a PendingIntent object to a Service, an Activity or BroadcastReceiver, at a time of our choosing. The simplest way to do that is using the set method:

void set(int type, long triggerAtMillis, PendingIntent operation)

When we set an alarm, we must also specify a type flag—the first parameter to the set method. The type flag sets the conditions under which the alarm should fire and which clock to use for our schedule.

There are two conditions and two clocks, resulting in four possible type settings.

The first...

Canceling alarms


Once the alarm is set, it can be canceled very easily by invoking the AlarmManger.cancel method with an intent that matches the alarm that we want to cancel.

The process of matching uses the filterEquals method of Intent, which compares the action, data, type, class, component, package, and categories of both Intent to test for equivalence. Any extras we may have set in the Intent are not taken into account.

In the following code, we will show you how to create an alarm that fires off in 1 hour and the cancel code to dismiss it using different intent instances:

// Function to set the Alarm
void set1HourAlarm(long time) {
  AlarmManager am= (AlarmManager) getSystemService(ALARM_SERVICE);
  long time = in1HourTime();
  am.set(AlarmManager.RTC, time, createPendingIntent(time));
}

// Cancel the alarm
void cancel1HourAlarm(long time) {
  AlarmManager am= (AlarmManager) getSystemService(ALARM_SERVICE);
  // Remove the alarms matching the Intent
  am.cancel(createPendingIntent(time...

Scheduling repeating alarms


As well as setting a one-off alarm, we have the option to schedule repeating alarms using setRepeating() and setInexactRepeating(). Both methods take an additional parameter that defines the interval in milliseconds at which to repeat the alarm. Generally, it is advisable to avoid setRepeating() and always use setInexactRepeating(), allowing the system to optimize device wake-ups and giving more consistent behavior on devices running different Android versions:

   void setRepeating(
       int type, long triggerAtMillis,
       long intervalMillis, PendingIntent operation);

   void setInexactRepeating(
       int type, long triggerAtMillis,
       long intervalMillis, PendingIntent operation)

AlarmManager provides some handy constants for typical repeat intervals:

   AlarmManager.INTERVAL_FIFTEEN_MINUTES
   AlarmManager.INTERVAL_HALF_HOUR
   AlarmManager.INTERVAL_HOUR
   AlarmManager.INTERVAL_HALF_DAY
   AlarmManager.INTERVAL_DAY

Let's now build up an example that...

Scheduling an alarm clock


From API Level 21, setAlarmClock, which sets a new alarm and displays a status bar alarm icon, was introduced in the AlarmManager class:

 void setAlarmClock(AlarmClockInfo info, PendingIntent operation)

In the next example we are going to create an alarm clock that goes off tomorrow at 10:00 pm:

Intent intent = new Intent("my_clock_alarm");
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 1);
calendar.set(Calendar.HOUR_OF_DAY, 22);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);

PendingIntent broadcast = PendingIntent.getBroadcast(
                                   AlarmClockActivity.this, 0, intent, 
                                   PendingIntent.FLAG_UPDATE_CURRENT);

// Only applies to newer versions
If ( Build.VERSION.SDK_INT >= 21 ) {

  AlarmClockInfo alarmInfo = new AlarmClockInfo(          
     calendar.getTimeInMillis(),    
     // Create a Pending intent to show Alarm Details
     createShowDetailsPI())...

Handling alarms


So far we have learned how to schedule exact and inexact alarms over the AlarmManager Service singleton, so at this point we are ready to take a look at how to handle the alarm in any Android application component.

Essentially, we can schedule anything that can be started with a PendingIntent, which means we can use alarms to start Activities, Services, and BroadcastReceivers. To specify the target of our alarm, we need to use the static factory methods of PendingIntent:

PendingIntent.getActivities(Context, int,Intent[],int)
PendingIntent.getActivity(Context,int, Intent, int)
PendingIntent.getService(Context,int, Intent, int)
PendingIntent.getBroadcast(Context,int, Intent, int)

All static methods offered to create a pending intent, receiving as arguments a Context object, an integer request code to identify the pending intent, an Intent or an array of Intents that will be delivered to the component, and finally an integer to specify the PendingIntent flags.

The PendingIntent...

Handling alarms with Activities


Starting an Activity from an alarm is as simple as registering the alarm with a PendingIntent created by invoking the static getActivity method of PendingIntent.

When the alarm is delivered, the Activity will be started and brought to the foreground, displacing any app that was currently in use. Keep in mind that this is likely to surprise and perhaps annoy users!

When starting Activities with alarms, we will probably want to set Intent.FLAG_ACTIVITY_CLEAR_TOP; so that if the application is already running, and our target Activity is already on the back stack, the new intent will be delivered to the old Activity and all the other activities on top of it will be closed:

   Intent intent = new Intent(context, HomeActivity.class);
   intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
   PendingIntent pending = PendingIntent.getActivity(
       Context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

Not all Activities are suited to being started with getActivity. We...

Handling alarms with BroadcastReceiver


We met BroadcastReceiver already in Chapter 5, Interacting with Services, where we used it in an Activity to receive broadcasts from a Service. In this section, we'll use BroadcastReceiver to handle alarms set on the AlarmManager.

BroadcastReceivers can be registered and unregistered dynamically at runtime like we did in Chapter 5, Interacting with Services, with Service, or statically in the Android manifest file with a <receiver> element, and can receive alarms regardless of how they are registered.

It is more common to use a statically registered receiver for alarms, because these are known to the system and can be invoked by alarms to start an application if it is not currently running.

Let's implement a static defined BroadcastReceiver that is able to dispatch an SMS to a phone number when an alarm sounds. First we will define our BroadcastReceiver in the manifest file:

<receiver android:name=".chapter6.SMSDispacther">
  <intent-filter...

Handling alarms with Services


Just like starting Activities, starting a Service from an alarm involves scheduling an appropriate PendingIntent instance, this time using the static getService method:

Intent intent = new Intent(this,SMSDispatcherIntentService.class);
intent.putExtra(SMSDispatcherIntentService.TO_KEY, phoneNumber);
intent.putExtra(SMSDispatcherIntentService.TEXT_KEY, text);
PendingIntent service = PendingIntent.getService(
   context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
am.set(AlarmManager.RTC_WAKEUP, time, service);

As you already know, the Service should be globally defined on the Android Manifest with a service element. Given that we are calling it explicitly using the class name, we only need to define the service class:

<service android:name=".chapter6.SMSDispatcherIntentService" >
</service>

We almost certainly want our Service to do its work off the main thread, so sending work to an IntentService this way seems ideal, and an IntentService will also...

Resetting alarms after a system reboot


The AlarmManager service is a convenient class to schedule working on your Android application; however, when the device shuts down or reboots, all your alarms will be lost since the system does not retain them between system restarts.

To reset the alarm, we should persist your alarms and create a BroadcastReceiver that sets our alarms whenever a system boot happens:

public class BootBroadcastReceiver extends BroadcastReceiver {

  @Override
  public void onReceive(Context context, Intent intent) {
    // Retrieve the persisted alarms
    List<SMSSchedule> persistedAlarms = getStoredSchedules();
    // Set again the alarms
    ...
  }
  List<SMSSchedule> getStoredSchedules() {...}
}

In order to store our alarms, we created a POJO class SMSSchedule as the model for our schedules.

Second, in the Android Manifest we have to register our BroadcastReceiver to receive the boot event:

<receiver
    android:name=".chapter6.BootBroadcastReceiver"
...

Applications of AlarmManager


AlarmManager allows us to schedule work to run without user intervention.

This means that we can arrange to do work pre-emptively, for example, to prepare data that our application will need to present to the user when they next open the application, or to alert the user to new or updated information with notifications.

Ideal use cases include things like periodically checking for new e-mails, SMS scheduling, time notifications, periodic data processing, downloading new editions of periodical publications (for example, daily newspapers and magazines), or uploading data from the device to a cloud backup service.

The AlarmManager is able to start future work effectively but the API should be used carefully to keep your application battery power consumption at low levels. To achieve that, the developer should try to keep the alarm frequency under certain levels and use the exact set functions that force the device to wake up only in cases where it is really necessary...

Summary


In this chapter, we learned to schedule work for our applications to perform at some time in the distant future, either as a one-shot operation or at regular intervals.

We learned to set alarms relative to the system clock or real time, how to wake the device up from a deep sleep and doze mode, how to cancel alarms when we no longer need them, and how to set exact alarms on the most recent Android versions.

In the meantime, we introduced the reader to Doze Mode, a new power management feature that saves battery cycles by deferring jobs and tasks to a maintenance window. We learned how to test our alarms taking into account the new power management states introduced by the doze mode.

We learned how to debug alarms created with AlarmManager and how to analyze the information printed from the dumpsys commands.

Our exploration covered various options for responding to alarms, including bringing an Activity to the foreground or doing work directly in a BroadcastReceiver, synchronously or...

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Asynchronous Android Programming - Second Edition
Published in: Jul 2016Publisher: PacktISBN-13: 9781785883248
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Steve Liles

Steve Liles is a self-confessed geek and has been an Android fan since the launch day of the G1. When he isn't at work building publishing systems and apps for newspapers and magazines, you'll find him tinkering with his own apps, building 3D printers, or playing RTS games. He is currently working with a start-up to build an advertising system that links the print and digital worlds using computer vision on Android and iOS devices.
Read more about Steve Liles