Advertisement
Android SDK

Android Fundamentals: Scheduling Recurring Tasks

by

Many applications have a need for regular, background actions to take place. For instance, in the "TutList" application we've been building over a recent series of tutorials, the content list is stale until the user initiates a refresh from the options menu. Why can't the application simply update its data at regular intervals? Well, it can and it should. Let's implement this new feature right now!

In our last tutorial, Android Fundamentals: Downloading Data With Services, you learned how to download data off the UI thread and independent of any particular activity. This is an efficient way to download data, while avoiding duplicate downloads and restarts. Now you're going to learn how to download content without the user even asking.

Step 0: Getting Started

This tutorial assumes you start where our last tutorial left off. You can download that code and work from it, or you can simply download or view the code provided with this tutorial and follow along. The choice is yours. We're on Version Code 7. The "tags" view of the source viewer has each version, for convenience.

Step 1: Android Alarms

The Android SDK has an alarm system for applications to use. The alarm mechanism allows events, specifically broadcast intents, to be triggered at a specific time or interval. Alarms can be one time or recurrent. Recurrent alarms can be scheduled at a very specific time, or set to allow the system to trigger them at an approximate time, convenient with other system events. Alarms can either wake the device up or not. And, finally, alarms can be scheduled based on the time since the device last booted up (good for short duration alarms) or based on the wall clock time.

We know that the Mobiletuts+ tutorials are posted daily at around 11:30 GMT. So, we can easily schedule the alarm event for our application to trigger daily at 11:45 GMT to pick up new content. That said, we don't want every single device to start downloading from the Mobiletuts+ server at the same time, so we'll also use the approximate repeating type, called inexact repeating.

Imagine if you had a popular app with a million users. Now imagine if that app requested data from the same server at the exact same time. In fact, with that amount of users, you'd likely want to spread out the updates over several hours or more. You'll see this behavior on many large scale systems. One way to handle this would be to have a base trigger time like 11:45 GMT, and then add a random number of minutes or hours to it in order to spread out the “mass downloading” across many devices.

Step 2: Creating a BroadcastReceiver

In order to listen for and react to broadcast intents that are triggered by alarms, you must create a broadcast receiver and register it with the system. To do this, create a new class within your application called AlarmReceiver and have it extend BroadcastReceiver. We've placed this in a new package, called com.mamlambo.tutorial.tutlist.receiver, for organizational purposes. The broadcast receiver will have just one job to do: start the service for downloading the XML feed. You already know how to do this from the <a href="last tutorial in this series, so here's the broadcast receiver in its entirety:

public class AlarmReceiver extends BroadcastReceiver {

    private static final String DEBUG_TAG = "AlarmReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(DEBUG_TAG, "Recurring alarm; requesting download service.");
        // start the download
        Intent downloader = new Intent(context, TutListDownloaderService.class);
        downloader.setData(Uri
                .parse("http://feeds.feedburner.com/MobileTuts?format=xml"));
        context.startService(downloader);
    }

}

Step 3: Registering a BroadcastReceiver

Broadcast receivers are usually registered within the Android manifest file. This way, they are active without running any code that would call the registerReceiver() method of the Context class. Broadcast receivers are registered with the <receiver> tag placed inside the application tag, like so:

<receiver
    android:name=".receiver.AlarmReceiver"></receiver>

Often, broadcast receivers meant to be used by other applications will also use an intent-filter. But as we'll reference this broadcast receiver by class name and have no need for other applications to use it, the registration is simple—just the receiver.

Step 4: Setting an Alarm

Android alarms are set using the AlarmManager service. This is a system service and, as such, can't just be instantiated like a regular Java class. Instead, you must use the getSystemService() method call to retrieve an instance of AlarmManager and create a properly configured intent for use with a broadcast (the type of intent the AlarmManager works with). Finally, you must determine the time, in milliseconds, until 1145 GMT will occur in order to the alarm correctly. Here's a single method called setRecurringAlarm() that encapsulates these features. This method is found in the TutListFragment class:

private void setRecurringAlarm(Context context) {

    // we know mobiletuts updates at right around 1130 GMT.
    // let's grab new stuff at around 11:45 GMT, inexactly
    Calendar updateTime = Calendar.getInstance();
    updateTime.setTimeZone(TimeZone.getTimeZone("GMT"));
    updateTime.set(Calendar.HOUR_OF_DAY, 11);
    updateTime.set(Calendar.MINUTE, 45);
    
    Intent downloader = new Intent(context, AlarmReceiver.class);
    PendingIntent recurringDownload = PendingIntent.getBroadcast(context,
            0, downloader, PendingIntent.FLAG_CANCEL_CURRENT);
    AlarmManager alarms = (AlarmManager) getActivity().getSystemService(
            Context.ALARM_SERVICE);
    alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP,
            updateTime.getTimeInMillis(),
            AlarmManager.INTERVAL_DAY, recurringDownload);
}

The PendingIntent object you see here is just a way to wrap an intent into an object that can send the encapsulated intent later from outside the current application.

The setRecurringAlarm() method should be called from a reasonable place. We've modified the application to call this method the first time the tutorial list is refreshed:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == refreshMenuId) {
        getActivity().startService(item.getIntent());

        // also set alarm
        Context context = getActivity().getApplicationContext();
        setRecurringAlarm(context);
    }
    return true;
}

After the user refreshes the list once, it will update automatically every day. There are certainly other places you could initiate this alarm. Don't worry about multiple alarms. This replaces any previous alarm setting with the same intent.

Step 5: Cancelling an Alarm [Optional]

Cancelling an alarm is done via the cancel() method of AlarmManager. It takes a single parameter, a PendingIntent. You don't have to keep the instance of PendingIntent around. Instead, simply create a new one with the same parameters. The intent will be matched by the system and any previously set alarm with the same intent parameters will be cancelled.

Step 6: Testing Alarms [Bonus]

Testing 24-hour repeating alarms is an exercise in patience. Instead, testing alarms can be best done by using a short interval. Alarms shouldn’t be set at extremely short intervals, though, or the Android system tends to throttle them and invalidate your tests. As such, we'd recommend either using the 15 minute interval (INTERVAL_FIFTEEN_MINUTES), or using a non-recurring time with the set() method and setting the time to the past. This way, the alarm will trigger immediately.

Conclusion

You have learned how to schedule single and recurring alarms in this quick tutorial. To support this functionality, you've also learned some of the basics behind one of the three primary intent types in Android: the broadcast intent. Used together, the simple “TutList” application now downloads fresh tutorial content once a day, several minutes after the typical publishing time from the MobileTuts+ website.

About the Authors

Mobile developers Lauren Darcey and Shane Conder have coauthored several books on Android development: an in-depth programming book entitled Android Wireless Application Development and Sams Teach Yourself Android Application Development in 24 Hours. When not writing, they spend their time developing mobile software at their company and providing consulting services. They can be reached at via email to androidwirelessdev+mt@gmail.com, via their blog at androidbook.blogspot.com, and on Twitter @androidwireless.

Need More Help Writing Android Apps? Check out our Latest Books and Resources!

Buy Android Wireless Application Development, 2nd Edition  Buy Sam's Teach Yourself Android Application Development in 24 Hours  Mamlambo code at Code Canyon

Related Posts
  • Code
    Android SDK
    Create a Music Player on Android: Song Playback0d63m preview image@2x
    In this series, we are creating a music player on Android using the MediaPlayer and MediaController classes. In the first part, we created the app and prepared the user interface for playback. We presented the list of songs on the user device and specified a method to execute when the user makes a selection. In this part of the series, we will implement a Service class to execute music playback continuously, even when the user is not directly interacting with the application.Read More…
  • Code
    Android SDK
    Using the Accelerometer on Android06lom preview image@2x
    In this tutorial, we are going to explore how to use the accelerometer, one of the many hardware sensors of modern smartphones, in an Android application. I'll explain what an accelerometer is and why it may be something you want to take advantage of in your Android applications.Read More…
  • Code
    iOS SDK
    Networking with NSURLSession: Part 2E548b preview image@2x
    In the previous tutorial, I introduced you to NSURLSession. I talked about the advantages it has over NSURLConnection and how to use NSURLSession for simple tasks, such as fetching data from a web service and downloading an image from the web. In this tutorial, we'll take a closer look at the configuration options of NSURLSession and how to cancel and resume a download task. We've got a lot of ground to cover so let's get started.Read More…
  • Code
    Android SDK
    Android SDK: Next StepsAndroid preview@2x
    In this series, we've begun learning how to develop Android applications from scratch. We started exploring the development tools, got acquainted with the basic elements in an application project, looked at user interface design, interactivity, resources, and data, and we've also took a closer look at what happens when your application is running. What we've covered so far should put you in a good position to get started creating functional Android applications, but Android has a lot more to offer so the range of possibilities is virtually endless. You may therefore struggle to choose what to learn next. In this part, we'll wrap up the series by pointing out some possible directions for future learning. After this, the final part will be a quiz on what we covered throughout the series.Read More…
  • Code
    Android SDK
    Android SDK: Common Android ComponentsAndroid preview@2x
    In this series we are learning the essential features of Android development that you need to know to start building apps. So far, we've looked at the structure and typical elements in an Android app, including user interface elements and data storage. You can use what we covered already to start creating your own apps. But before you do, we will run through some common Android components in this tutorial, then have a quick look at the SDK samples in the next tutorial.Read More…
  • Code
    Android SDK
    Android Barometer Logger: Acquiring Sensor DataAndroid sdk barometer sensor data
    Smartphones are full of hardware sensors, such as accelerometers, light sensors, gyroscopes, and so on. Learn how to gather data from these sensors by way of implementing a barometric logger.Read More…