Video icon 64
Learning to code? Skill up faster with our practical video courses. Start your free trial today.
Advertisement

Android Fundamentals: Scheduling Recurring Tasks

This post is part of a series called Android Fundamentals.
Android Fundamentals: Downloading Data With Services
Android Fundamentals: Status Bar Notifications

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

Advertisement