You will find in this document


1. Basic research on Android AlarmManager API
2. Useful api references while working with AlarmManager
3. Not-so-intuitive truths about AlarmManager
4. Sample code
5. Downloadable Sample project
6. Finally what I call the AlarmManager Predicates

the api

This is done through an intent.

what about invoking an activity? or calling a service?

If your alarm receiver called Context.startService(), it is possible that the phone will sleep before the requested service is launched. To prevent this, your BroadcastReceiver and Service will need to implement a separate wake lock policy to ensure that the phone continues running until the service becomes available.


Intent intent = new Intent(AlarmController.this, 
              YourBroadcastReceiver.class);

PendingIntent pi = 
    PendingIntent.getBroadcast(AlarmController.this,
            0, intent, 0);

// Tell the user about what we did.
if (mToast != null) {
        mToast.cancel();
}
mToast = Toast.makeText(<some-context>, 
       R.string.one_shot_scheduled,
       Toast.LENGTH_LONG);
mToast.show();

Toast API


LENGTH_LONG
LENGTH_SHORT

Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, 30);

// Schedule the alarm!
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);

Intent intent = new Intent(AlarmController.this, RepeatingAlarm.class);
PendingIntent sender = PendingIntent.getBroadcast(AlarmController.this,
                 0, intent, 0);
            
// And cancel the alarm.
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
am.cancel(sender);

alarm boot

Search Google for: alarm boot

Search Android Developers Group for: alarm boot

Search Android Beginers Group for: alarm boot

Search Google Code for: alarm boot

Search Android Issues Database for: alarm boot

alarm not set

Search Google for: alarm not set

Search Android Developers Group for: alarm not set

Search Android Beginers Group for: alarm not set

Search Google Code for: alarm not set

Search Android Issues Database for: alarm not set

alarm preferences

Search Google for: alarm preferences

Search Android Developers Group for: alarm preferences

Search Android Beginers Group for: alarm preferences

Search Google Code for: alarm preferences

Search Android Issues Database for: alarm preferences

android saving alarm manager settings

Search for: android saving alarm manager settings

alarms lost

Search Google for: alarms lost

Search Android Developers Group for: alarms lost

Search Android Beginers Group for: alarms lost

Search Google Code for: alarms lost

Search Android Issues Database for: alarms lost

Nice post on lost alarms

Read this long thread on stopping an app and alarms

rtc_wakeup

Search Google for: rtc_wakeup

Search Android Developers Group for: rtc_wakeup

Search Android Beginers Group for: rtc_wakeup

Search Google Code for: rtc_wakeup

Search Android Issues Database for: rtc_wakeup

For cancelling intents, two intents are the same for the purposes of intent resolution (filtering). That is, if their action, data, type, class, and categories are the same. This does not compare any extra data included in the intents.

This equivalence is defined by filterEquals


private void sendAlarmOnce()
{
   //Get the instance in time that is 
   //30 secs from now.
   Calendar cal = Utils.getTimeAfterInSecs(30);
   
   //If you want to point to 11:00 hours today.
   //Calendar cal = Utils.getTodayAt(11);

   //Print to the debug view that we are 
   //scheduling at a specific time
   String s = Utils.getDateTimeString(cal);
   appendText("Schdeduling alarm at: " + s);
   
   //Get an intent to invoke
   //TestReceiver class
   Intent intent = 
      new Intent(this, TestReceiver.class);
   intent.putExtra("message", "Single Shot Alarm");

   PendingIntent pi = 
      PendingIntent.getBroadcast(
        this,    //context
        0,       //request id - not used
        intent,    //intent to be delivered 
        0);       //pending intent flags
   // Schedule the alarm!
   AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
   am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pi);
}

private void sendRepeatingAlarm()
{
   Calendar cal = Utils.getTimeAfterInSecs(30);
   //Calendar cal = Utils.getTodayAt(11);
   String s = Utils.getDateTimeString(cal);
   appendText("Schdeduling alarm at: " + s);
   
   //Get an intent to invoke
   //TestReceiver class
   Intent intent = 
      new Intent(this, TestReceiver.class);
   intent.putExtra("message", "Repeating Alarm");

   PendingIntent pi = 
      PendingIntent.getBroadcast(
        this,    //context
        0,       //request id - not used
        intent,    //intent to be delivered 
        0);       //pending intent flags
   // Schedule the alarm!
   AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
   am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5*1000, pi);
   //am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pi);
}

Start the first alarm 30 secs from now. Then repeat it every 5 secs.

The code also inclues a commented out section to start the alarm at 11:00 hrs today and you can then repeat it at the interval you would like. Notice however that when you use he wake option you will be waking the device.

AlarmManager partial wakeup?

Search Google for: AlarmManager partial wakeup?

Search Android Developers Group for: AlarmManager partial wakeup?

Search Android Beginers Group for: AlarmManager partial wakeup?

Search Google Code for: AlarmManager partial wakeup?

Search Android Issues Database for: AlarmManager partial wakeup?


private void cancelRepeatingAlarm()
{
   //Get an intent to invoke
   //TestReceiver class
   Intent intent = 
      new Intent(this, TestReceiver.class);
   intent.putExtra("message", "Repeating Alarm");

   PendingIntent pi = 
      PendingIntent.getBroadcast(
        this,    //context
        0,       //request id - not used
        intent,    //intent to be delivered 
        0);       //pending intent flags
   // Schedule the alarm!
   AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
   am.cancel(pi);
}

flag_one_shot

Search Google for: flag_one_shot

Search Android Developers Group for: flag_one_shot

Search Android Beginers Group for: flag_one_shot

Search Google Code for: flag_one_shot

Search Android Issues Database for: flag_one_shot

You need to have something unique in the intent action, type, data, or categories to get different pending intent objects.

Having data (action, type, Uri, categories, component) that is unique within the intent is the way to do this. You can also use the request code to disambiguate them.

request id is ignored as far as delivering the pending intent goes, because broadcasts don't (currently) have requests codes. It is not ignored for intent matching, but taken as another identifying part of the pending intent. And none of this has anything specifically to do with the alarm manager, which just gets pending intents, and calls equals() and send() on them.

This is actually just a general Intent thing -- if you make a subclass of Intent and pass it to pretty much any system API, your subclass is going to be lost as it moves across processes. One could argue that Intent itself should be final, but I am loath to make things final when they don't absolutely have to be, and end up in annoying situations like String. (And anyway, the SDK is published, so it is too late to make any of these APIs final anyway.)

The above is taken from the following topic

pendingintent requestid

Search Google for: pendingintent requestid

Search Android Developers Group for: pendingintent requestid

Search Android Beginers Group for: pendingintent requestid

Search Google Code for: pendingintent requestid

Search Android Issues Database for: pendingintent requestid

Cancel Alarms

Search Google for: Cancel Alarms

Search Android Developers Group for: Cancel Alarms

Search Android Beginers Group for: Cancel Alarms

Search Google Code for: Cancel Alarms

Search Android Issues Database for: Cancel Alarms


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

    //pending intent flags
    //PendingIntent.FLAG_ONE_SHOT);        
    return pi;
}

I would have thought, when I am scheduling an intent for a single shot alarm, that I could use flag_one_shot. The idea is that the scheduled intent will be taken off after that.

however it seem to work fine even for a repeating alarm. Not sure what gives. but again I haven't done lot of testing.


/*
 * An alarm can invoke a broadcast request
 * at a specified time.
 * The name of the broadcast receiver is explicitly
 * specified in the intent.
 */
public void sendAlarmOnce()
{
    //Get the instance in time that is 
    //30 secs from now.
    Calendar cal = Utils.getTimeAfterInSecs(30);
    
    //If you want to point to 11:00 hours today.
    //Calendar cal = Utils.getTodayAt(11);

    //Print to the debug view that we are 
    //scheduling at a specific time
    String s = Utils.getDateTimeString(cal);
    mReportTo.reportBack(tag, "Schdeduling alarm at: " + s);
    
    //Get an intent to invoke
    //TestReceiver class
    Intent intent = 
        new Intent(mContext, TestReceiver.class);
    intent.putExtra("message", "Single Shot Alarm");

    PendingIntent pi = 
        PendingIntent.getBroadcast(
          mContext,     //context
          1,            //request id, used for disambiguating this intent
          intent,       //intent to be delivered 
          PendingIntent.FLAG_ONE_SHOT); //pending intent flags
    // Schedule the alarm!
    AlarmManager am = 
        (AlarmManager)
             mContext.getSystemService(Context.ALARM_SERVICE);
    
    am.set(AlarmManager.RTC_WAKEUP, 
            cal.getTimeInMillis(), 
            pi);
}

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

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

/*
 * An alarm can invoke a broadcast request
 * starting at a specified time and at 
 * regular intervals.
 * 
 * Uses the same intent as above
 * but a distinct request id to avoid conflicts
 * with the single shot alarm above.
 * 
 * Uses getDistinctPendingIntent() utility.
 */
public void sendRepeatingAlarm()
{
    Calendar cal = Utils.getTimeAfterInSecs(30);
    //Calendar testcal = Utils.getTodayAt(11);
    String s = Utils.getDateTimeString(cal);
    this.mReportTo.reportBack(tag,
        "Schdeduling Repeating alarm in 5 sec interval starting at: " + s);
    
    //Get an intent to invoke
    //TestReceiver class
    Intent intent = 
        new Intent(this.mContext, TestReceiver.class);
    intent.putExtra("message", "Repeating Alarm");

    PendingIntent pi = this.getDistinctPendingIntent(intent, 2);
    // Schedule the alarm!
    AlarmManager am = 
        (AlarmManager)
            this.mContext.getSystemService(Context.ALARM_SERVICE);
    
    am.setRepeating(AlarmManager.RTC_WAKEUP, 
            cal.getTimeInMillis(), 
            5*1000, //5 secs 
            pi);
}

/*
 * An alarm can be stopped by canceling the intent.
 * You will need to have a copy of the intent
 * to cancel it.
 * 
 * The intent needs to have the same signature
 * and request id.
 */
public void cancelRepeatingAlarm()
{
    //Get an intent 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);
    
    // Schedule the alarm!
    AlarmManager am = 
        (AlarmManager)
           this.mContext.getSystemService(Context.ALARM_SERVICE);
    am.cancel(pi);
    this.mReportTo.reportBack(tag,"You shouldn't see alarms again");
}

/*
 * Same intent cannot be scheduled multiple times.
 * If you do, only the last one will take affect.
 * 
 * Notice you are using the same request id.
 */
public void scheduleSameIntentMultipleTimes()
{
    //Get the instance in time that is 
    //30 secs from now.
    Calendar cal = Utils.getTimeAfterInSecs(30);
    Calendar cal2 = Utils.getTimeAfterInSecs(35);
    Calendar cal3 = Utils.getTimeAfterInSecs(40);
    Calendar cal4 = Utils.getTimeAfterInSecs(45);
    
    //If you want to point to 11:00 hours today.
    //Calendar cal = Utils.getTodayAt(11);

    //Print to the debug view that we are 
    //scheduling at a specific time
    String s = Utils.getDateTimeString(cal);
    mReportTo.reportBack(tag, "Schdeduling alarm at: " + s);
    
    //Get an intent to invoke
    //TestReceiver class
    Intent intent = 
        new Intent(mContext, TestReceiver.class);
    intent.putExtra("message", "Single Shot Alarm");

    PendingIntent pi = this.getDistinctPendingIntent(intent, 1);
    
    // Schedule the alarm!
    AlarmManager am = 
        (AlarmManager)
             mContext.getSystemService(Context.ALARM_SERVICE);
    
    am.set(AlarmManager.RTC_WAKEUP, 
            cal.getTimeInMillis(), 
            pi);
    
    am.set(AlarmManager.RTC_WAKEUP, 
            cal2.getTimeInMillis(), 
            pi);
    am.set(AlarmManager.RTC_WAKEUP, 
            cal3.getTimeInMillis(), 
            pi);
    am.set(AlarmManager.RTC_WAKEUP, 
            cal4.getTimeInMillis(), 
            pi);
}

/*
 * Same intent can be scheduled multiple times
 * if you change the request id on the pending intent.
 * Request id identifies an intent as a unique intent.
 */
public void scheduleDistinctIntents()
{
    //Get the instance in time that is 
    //30 secs from now.
    Calendar cal = Utils.getTimeAfterInSecs(30);
    Calendar cal2 = Utils.getTimeAfterInSecs(35);
    Calendar cal3 = Utils.getTimeAfterInSecs(40);
    Calendar cal4 = Utils.getTimeAfterInSecs(45);
    
    //If you want to point to 11:00 hours today.
    //Calendar cal = Utils.getTodayAt(11);

    //Print to the debug view that we are 
    //scheduling at a specific time
    String s = Utils.getDateTimeString(cal);
    mReportTo.reportBack(tag, "Schdeduling alarm at: " + s);
    
    //Get an intent to invoke
    //TestReceiver class
    Intent intent = 
        new Intent(mContext, TestReceiver.class);
    intent.putExtra("message", "Single Shot Alarm");

    // Schedule the alarms!
    AlarmManager am = 
        (AlarmManager)
             mContext.getSystemService(Context.ALARM_SERVICE);
    
    am.set(AlarmManager.RTC_WAKEUP, 
            cal.getTimeInMillis(), 
            getDistinctPendingIntent(intent,1));
    
    am.set(AlarmManager.RTC_WAKEUP, 
            cal2.getTimeInMillis(), 
            getDistinctPendingIntent(intent,2));
    am.set(AlarmManager.RTC_WAKEUP, 
            cal3.getTimeInMillis(), 
            getDistinctPendingIntent(intent,3));
    am.set(AlarmManager.RTC_WAKEUP, 
            cal4.getTimeInMillis(), 
            getDistinctPendingIntent(intent,4));
}

/*
 * It is not the alarm that matters 
 * but the pending intent.
 * Even with a repeating alarm for an intent,
 * if you schedule the same intent again
 * for one time, the later one takes affect.
 * 
 * It is as if you are setting the 
 * alarm on an existing intent multiple
 * times and not the other way around.
 */
public void alarmIntentPrimacy()
{
    Calendar cal = Utils.getTimeAfterInSecs(30);
    String s = Utils.getDateTimeString(cal);
    this.mReportTo.reportBack(tag,
        "Schdeduling Repeating alarm in 5 sec interval starting at: " + s);
    
    //Get an intent to invoke
    //TestReceiver class
    Intent intent = 
        new Intent(this.mContext, TestReceiver.class);
    intent.putExtra("message", "Repeating Alarm");

    PendingIntent pi = getDistinctPendingIntent(intent,0); 
    AlarmManager am = 
        (AlarmManager)
            this.mContext.getSystemService(Context.ALARM_SERVICE);
    
    this.mReportTo.reportBack(tag,"Setting a repeat alarm 5 secs duration");
    am.setRepeating(AlarmManager.RTC_WAKEUP, 
            cal.getTimeInMillis(), 
            5*1000, //5 secs 
            pi);

    this.mReportTo.reportBack(tag,"Setting a onetime alarm on the same intent");
    am.set(AlarmManager.RTC_WAKEUP, 
            cal.getTimeInMillis(), 
            pi);
    this.mReportTo.reportBack(tag,"The later alarm, one time one, takes precedence");
}

public static Calendar getTimeAfterInSecs(int secs)
{
    Calendar cal = Calendar.getInstance();
    cal.add(Calendar.SECOND,secs);
    return cal;
}
public static Calendar getCurrentTime()
{
    Calendar cal = Calendar.getInstance();
    return cal;
}
public static Calendar getTodayAt(int hours)
{
    Calendar today = Calendar.getInstance();
    Calendar cal = Calendar.getInstance();
    cal.clear();
    
    int year = today.get(Calendar.YEAR);
    int month = today.get(Calendar.MONTH);
    //represents the day of the month
    int day = today.get(Calendar.DATE);
    cal.set(year,month,day,hours,0,0);
    return cal;
}
public static String getDateTimeString(Calendar cal)
{
    SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy hh:mm:ss");
    df.setLenient(false);
    String s = df.format(cal.getTime());
    return s;
}

You can download a sample project here (zip file link)

This zip file has all of the code above and also a driver (a set of menus) to invoke each of the functions mentioned above.

You can see the logcat to see how it behaves.

The project also includes the test receiver.

  1. Alarms are not persistent across boots. What ever alarms you have set through the alarm manager will be lost when the device reboots.
  2. You will need to persist alarm parameters yourself if you would like to retain them beyond device reboots. You will need to listen to broadcast boot event and also time change events to reset these alarms as needed.
  3. Alarms and pending intents (even intents for that matter) are not independent
  4. A given pending intent cannot be used for multiple alarms. The last alarm will override the previous alarms
  5. It is almost like an alarm is set, repeating or otherwise, on a unique pending intent, although the api gives the impression that you are indepenently scheduling alarms with an intent. It appears it is the exact opposite. You take an intent and specify when that intent will go off.
  6. You can not really derive from an intent because when the intent crosses process or package boundaries and reincarnated it will not have the class definition of the derived class
  7. The implication of the intent based cancel api is that when you use or persist alarms you will also need to persist intents so that those alarms can be cancelled at a later time.
  8. It is as if the api would have been more inuitive if it said "pendingIntent.setAlarm(...)" as opposed to "AlarmManager.setAlarm(pendingintent)" followed by "pendingIntent.cancelAlarm()". The meaning would have been more direct.
  9. You cannot new a pending intent. You really locate a pending intent with an option to reuse/update etc.
  10. An intent is uniquely distinguihed by its action, datauri, and its category. This is specified in the "filterEquals()" api of the intent class.
  11. A pending intent is further qualified (in addition to the base intent it depended on) by the request code.
  12. It appears that pending intents are kept in a pool that are reused

flag_one_shot

Search Google for: flag_one_shot

Search Android Developers Group for: flag_one_shot

Search Android Beginers Group for: flag_one_shot

Search Google Code for: flag_one_shot

Search Android Issues Database for: flag_one_shot

If I create a pending intent twice with the same equality (which doesnt include the extras) then as I understand I get back a reference to the same "token" or a pending intent. This will let me cancel it!!!

Am I then to understand that I am really "fetching" an intent and not creating it??

The docs say that this will cancel the current pending intent. When you dont do this probably the extra data in the new intent will not make it to the previous intent and you get back a reference to the previous pending intent.

the doc advises that you will do this so that you prevent previous intents from firing because in your mind that firing is invalid because you want to do somethign different with the newly acquired extras.

If you dont mind firing the old ones, then we are advised, to use the flag_update_current

This will return null if the intent doesnt exist. Perhaps one will use this to locate a previous pending intent and cancel it.

The docs say

if the described PendingIntent already exists, then keep it but its replace its extra data with what is in this new Intent. This can be used if you are creating intents where only the extras change, and don't care that any entities that received your previous PendingIntent will be able to launch it with your new extras even if they are not explicitly given to it.

This again shows that we are fundamentally locating an event when we issue a pending event. And in this case we are overloading with the new extras.

Significantly it seem to be wrong to think of a PendingIntent as a message!!

However when you attach a requestid to it, it seem to behave like a message

Pending Intent Dianne

Search Google for: Pending Intent Dianne

Search Android Developers Group for: Pending Intent Dianne

Search Android Beginers Group for: Pending Intent Dianne

Search Google Code for: Pending Intent Dianne

Search Android Issues Database for: Pending Intent Dianne

An interesting thread to look at on pending intents

Here is another discussion

No, I mean setting the explicit component to your broadcast receiver component on the Intent class. I strongly strongly recommend this for this kind of situation where you want someone to deliver some specific thing to a component in your app. Please read the Intent java doc for more info.

Different request codes is a perfectly fine way to do it.

That said... I would be -very- suspicious about the code you posted where you are effectively creating random identities for your pending intents. In most of the places where one uses a pending intent, you give it to something that holds on to it indefinitely, so you need to be able to recover the previous pending intent so you can give it back to have it unregistered. That is why the API works the way it does. This API is really not intended to generate tons of pending intents whose lifetime is not being managed.

As per the docs this seem to set it once and the cancel it right after sending it one time. This is a property of the pending intent and not the intent.

what happens if there is a pending intent already? will the new flags override those old flags??

Here is an implementation of the AlarmManagerService.java

It is available as


com/android/server/AlarmManagerService.java

Here is a link to various other implementations of services

set removelocked alarm

Search Google for: set removelocked alarm

Search Android Developers Group for: set removelocked alarm

Search Android Beginers Group for: set removelocked alarm

Search Google Code for: set removelocked alarm

Search Android Issues Database for: set removelocked alarm


160     public void setRepeating(int type, long triggerAtTime, long interval,  
 161             PendingIntent operation) { 
 162         if (operation == null) { 
 163             Slog.w(TAG, "set/setRepeating ignored because there is no intent"); 
 164             return; 
 165         } 
 166         synchronized (mLock) { 
 167             Alarm alarm = new Alarm(); 
 168             alarm.type = type; 
 169             alarm.when = triggerAtTime; 
 170             alarm.repeatInterval = interval; 
 171             alarm.operation = operation; 
 172  
 173             // Remove this alarm if already scheduled. 
 174             removeLocked(operation); 
 175  
 176             if (localLOGV) Slog.v(TAG, "set: " + alarm); 
 177  
 178             int index = addAlarmLocked(alarm); 
 179             if (index == 0) { 
 180                 setLocked(alarm); 
 181             } 
 182         } 
 183     }

As it turned out I was looking at the wrong method to understand the alarm manager behavior. I was overly focused on the "set" methods to understand it.

There is no indication in the set method that the "pendingintent" is a key on which the alarming timing is set.

However if we look at "cancel" method this becomes quite clear. A cancel operation takes a pendingintent and cancels the alarm. If we were allowed "multiple" alarms for a given pendign intent, what would be the meaning of cancelling an alarm based on a "pendingintent"?? Will it cancel all the alarms?? So in short for "cancel" to work to cancel an "alarm" the "alarm" and the corresponding "pending intent" must be "one to one".

Had I focused on "cancel" this would have become apparent lot more sooner. Well there it is.

See the updates to Android Alarm manager as of 2014 here