Chapter    17

Exploring the Alarm Manager

In Android an intent object is used to start a UI activity, a background service, or a broadcast receiver. Normally these intents are triggered by user actions. In Android you can also use alarms to trigger broadcast intents, mind you, only broadcast intents. The invoked broadcast receivers then can choose to start an activity or a service.

In this chapter you will learn about the alarm manager API. Alarm manager API is used to schedule a broadcast intent to go off at a particular time. We will refer to this process of scheduling a broadcast intent at a particular time as setting an alarm.

We will also show you how to schedule alarms that repeat at regular intervals. We will show you how to cancel alarms that are already set.

When an intent object is stored to be used at a later time, it is called a pending intent. As alarm managers use pending intents all the time you will get to see the usage and intricacies of pending intents as well in this chapter.

Setting Up a Simple Alarm

We will start the chapter with setting an alarm at a particular time and having it call a broadcast receiver. Once the broadcast receiver is invoked, you can use the information from Chapter 16 to perform both simple and long-running operations in that broadcast receiver.

Getting access to the alarm manager is simple and is shown in Listing 17-1.

Listing 17-1. Getting Access to an Alarm Manager

//In filename: SendAlarmOnceTester.java
AlarmManager am =
    (AlarmManager)
         anyContextObject.getSystemService(Context.ALARM_SERVICE);

The variable anyContextObject refers to a context object. For example, if you are invoking this code from an activity menu, the context variable will be the activity. To set the alarm for a particular date and time, you will need an instance in time identified by a Java Calendar object. Listing 17-2 shows a utility function that gives you a calendar object for some specified time instant after the current time.

Listing 17-2. A Few Useful Calendar Utilities

//In filename: Utils.java
public class Utils {
    public static Calendar getTimeAfterInSecs(int secs) {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.SECOND,secs);
        return cal;
    }
}

In the downloadable project for this chapter, you will see lot more calendar-based utilities to arrive at a time instance in a number of ways. Now, we need a receiver to set against the alarm that we are planning to set. A simple receiver is shown in Listing 17-3.

Listing 17-3. TestReceiver to Test Alarm Broadcasts

//In filename: TestReceiver.java
public class TestReceiver extends BroadcastReceiver  {
    private static final String tag = "TestReceiver";
    @Override
    public void onReceive(Context context, Intent intent)  {
        Log.d (tag, "intent=" + intent);
        String message = intent.getStringExtra("message");
        Log.d(tag, message);
    }
}

You will need to register this receiver in the manifest file using the <receiver> tag, as shown in Listing 17-4. Receivers are covered in detail in Chapter 16.

Listing 17-4. Registering a Broadcast Receiver

<!-- In filename: AndroidManifest.xml -->
<receiver android:name=".TestReceiver"/>

In Android an alarm is really a broadcast intent that is scheduled for a later time. What receiver component this intent should invoke is explicitly (through its classname) specified in the intent. Listing 17-5 shows an intent that can be used to invoke the broadcast receiver that we had in Listing 17-3.

Listing 17-5. Creating an Intent Pointing to TestReceiver

//In filename: SendAlarmOnceTester.java
Intent intent = new Intent(mContext, TestReceiver.class);
intent.putExtra("message", "Single Shot Alarm");

We also have an opportunity to load the intent with “extras” while creating this intent. Because an alarm manager stores an intent for a later use, we need to create a pending intent out of this intent of Listing 17-5. Listing 17-6 shows how to create a pending intent from a standard intent.

Listing 17-6. Creating a Pending Intent

//In filename: SendAlarmOnceTester.java
PendingIntent pendingIntent =
    PendingIntent.getBroadcast(
      mContext,    //context, or activity, or service
      1,           //request id, used for disambiguating this intent
      intent,      //intent to be delivered
      0);          //pending intent flags

Notice that we have asked the PendingIntent class to construct a pending intent that is suitable for a broadcast explicitly. The other variations of creating a pending intent are listed in Listing 17-7:

Listing 17-7. Multiple APIs for Creating a Pending Intent

//useful to start an activity
PendingIntent activityPendingIntent = PendingIntent.getActivity(..args..);
//useful to start a service
PendingIntent servicePendingIntent = PendingIntent.getService(..args..);

In Listing 17-7, arguments to the methods getActivity() and getService() are similar to the arguments to the getBroadcast() method in Listing 17-6. Note that alarms require a broadcast pending intent and not an activity pending intent or a service pending intent.

We will discuss the request id argument, which we set to 1 in Listing 17-6, in greater detail later in the chapter. Briefly, it is used to separate two intent objects that are equal in all other respects.

Pending intent flags have little or no influence on the alarm manager. Recommendation is to use no flags at all and use 0 for their values. These intent flags are typically useful in controlling the lifetime of the pending intent. However, in this case, the lifetime is maintained by the alarm manager. For example, to cancel a pending intent, you ask the alarm manager to cancel it.

Once we have the time instance in milliseconds as a Calendar object and the pending intent pointing to the receiver, we can set up an alarm by calling the set() method of the alarm manager. This is shown in Listing 17-8.

Listing 17-8. Using the Alarm Manager set() Method

//In filename: SendAlarmOnceTester.java
Calendar cal = Utils.getTimeAfterInSecs(30);
//...other code that gets the pendingintent etc
am.set(AlarmManager.RTC_WAKEUP,
        cal.getTimeInMillis(),
        pendingIntent);

The first argument to the set()method indicates the wakeup nature of the alarm and also the reference clock that we are going to be using for the alarm. Possible values for this argument are AlarmManager.RTC_WAKEUP, AlarmManager.RTC, AlarmManager.ELAPSED_REALTIME, AlarmManager.ELAPSED_REALTIME_WAKEUP.

The elapsed word in these constants refers to the time in milliseconds since the device is recently booted. So, it refers to the device clock. The RTC time refers to the human clock/time that you see on the device when you check your clock on the device. The WAKEUP word in these constants refers to the nature of the alarm, such as whether the alarm should wake up the device or just deliver it at the first opportunity when the device eventually wakes up. Taken together, the RTC_WAKEUP indicates the use of real-time clock and the device should wake up. The constant ELAPSED_REALTIME means use the device clock and don’t wake up the device; instead, deliver the alarm at the first opportunity.

When this method of Listing 17-8 is called, the alarm manager will invoke the TestReceiver in Listing 17-3, 30 seconds after the calendar time when the method was called and also wakes up the device if it is asleep.

Setting Off an Alarm Repeatedly

Let’s now consider how we can set an alarm that goes of repeatedly; see Listing 17-9.

Listing 17-9. Setting a Repeating Alarm

public void sendRepeatingAlarm() {
    Calendar cal = Utils.getTimeAfterInSecs(30);

    //Get an intent to invoke the receiver
    Intent intent = new Intent(this.mContext, TestReceiver.class);
    intent.putExtra("message", "Repeating Alarm");

    int requestid = 2;
    PendingIntent pi = this.getDistinctPendingIntent(intent, requestid);
    // Schedule the alarm!
    AlarmManager am =
        (AlarmManager)
            this.mContext.getSystemService(Context.ALARM_SERVICE);

    am.setRepeating(AlarmManager.RTC_WAKEUP,
            cal.getTimeInMillis(),
            5*1000, //5 secs repeat
            pi);
}

protected PendingIntent getDistinctPendingIntent(Intent intent, int requestId) {
    PendingIntent pi =
        PendingIntent.getBroadcast(
          mContext,     //context, or activity
          requestId,    //request id
          intent,       //intent to be delivered
          0);
    return pi;
}

Key elements of the code in Listing 17-9 are highlighted. A repeating alarm is set by invoking the setRepeating() method on the alarm manager object. The primary input to this method is a pending intent pointing to a receiver. We have used the same intent that was created in Listing 17-5, the one pointing to the TestReceiver. However, when we make a pending intent out of the intent in Listing 17-5, we alter the unique request code to a value of 2. If we don’t do this, we will see a bit of odd behavior which we shall explain now. Say we intend to invoke the same receiver through two different alarms: one alarm that goes off only once and another alarm that goes off repeatedly. Because both alarms target the same receiver they need to be using an intent that points to the same receiver. Two intents that point to the same receiver, without any other difference between them, is considered the same intent. So, when we tell the alarm manager to set the alarm on intent 1 as a one-time alarm and then set the alarm on intent 2 as a repeated alarm, we might be under the impression that they are two different alarms. Internally, however, both alarms point to the same intent value, as intent 1 and intent 2 are the same in their values. This is why an alarm is practically the same as its intent on which it is set (especially by value). As a result, the later alarm overrides the first alarm if the intents are equivalent.

Again, two intents are considered the same if they have the same action, type, data, categories, or class. The extras are not included in figuring out the uniqueness of intents. Further, two pending intents are considered the same if their underlying intents are the same and the request IDs match. Because we can use the request ID to distinguish two pending intents, the code in Listing 17-8 overcomes the similarity of source intents by using the request id argument. This request id argument to the PendingIntent API will separate one pending intent from the other pending intent when all else matches.

This all should make sense if you were to see the pending intent (by value not by its Java object reference) itself as the alarm on which you are setting different times.

Cancelling an Alarm

Code in Listing 17-10 is used to cancel an alarm.

Listing 17-10. Cancelling a Repeating Alarm

public void cancelRepeatingAlarm() {
    //Get an intent that was originally
    //used to invoke TestReceiver class
    Intent intent = new Intent(this.mContext, TestReceiver.class);

    //To cancel, extra is not necessary to be filled in
    //intent.putExtra("message", "Repeating Alarm");

    PendingIntent pi = this.getDistinctPendingIntent(intent, 2);

    // Cancel the alarm!
    AlarmManager am =
        (AlarmManager)
           this.mContext.getSystemService(Context.ALARM_SERVICE);
    am.cancel(pi);
}

To cancel an alarm, we have to construct a pending intent first and then pass it to the alarm manager as an argument to the cancel() method. However, you must pay attention to make sure that the PendingIntent is constructed the exact same way when setting the alarm, including the request code and targeted receiver.

In constructing the cancel intent, you can ignore the intent extras from the original intent (Listing 17-10), because intent extras don’t play a role in the uniqueness of an intent, and hence cancelling that intent.

Understanding Exactness of Alarms

Prior to API 19, Android fired the alarms as close as possible to the specified time. Since API 19, alarms that are close to each other are bundled for battery life. If you need the older behavior, there is a version of set() method called setExact(). There is also a method called setWindow() that allows room for efficiencies and also allows a guaranteed window. Similarly, the method setRepeating() is now inexact. Unlike the setExact() method, there is no exact version for setRepeating(). If you have such a need, you have to use the setExact() and repeat it yourself multiple times.

Understanding Persistence of Alarms

Another note on alarms is that they are not saved across device reboots. This means you will need to save the alarm settings and pending intents in a persistent store and reregister them based on device reboot broadcast actions, and possibly time-change broadcast actions (e.g., intent.ACTION_BOOT_COMPLETED, intent.ACTION_TIME_CHANGED, intent.ACTION_TIMEZONE_CHANGED).

References

The following references will help you learn more about the topics discussed in this chapter:

Summary

This chapter explored the Alarm Manager API, which you use to set up and cancel alarms. This chapter showed you how to connect an alarm to a broadcast service. This chapter also showed you how alarms are closely related to intents.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.55.18