Home > android > Android AlarmManager pain

Android AlarmManager pain

Never thought I’d be experiencing issues with such a simple task as setting recurring alarms, but it seems Android’s AlarmManager makes this more difficult than it should be.

I have a need for periodic alarms in my application to refresh data from the Internet. Pretty simple, just use AlarmManager.setRepeating or setInexactRepeating, right?

The basic issue is, there is no way for an application to know if its alarms are set.

Consider this:

  1. The user force stops my application. This clears the alarms, which is correct, because the user is very clearly saying that he does not want to run my application at this time (actually, it may not be correct, he just wants to free some memory, but I will let it pass for now).
  2. Then he runs one of the application’s activities again, which means that the application should enable scheduled updates again.
  3. To accomodate this, the code in my main activity should set the alarm somewhere in its onCreate / onStart / onResume callbacks.

Since my code has no way of knowing whether the alarm has already been set, this will schedule the next update at current time plus the update interval. This means that if the update interval is long (say, an hour or two), and the user opens the application between the updates, the scheduled actions may never take place.

Using persistent data storage doesn’t help, because if the application is killed, it won’t have a chance to clear its persistent storage, so it’ll think the alarm is set whereas it’s not.

My current solution is to schedule alarms one by one, keeping track of the next scheduled time in shared preferences. The code sets the alarm only if the next scheduled time point has already passed.

This is not perfect, because in the above scenario, the updates will resume only after 2x the update interval (one to set the next alarm, another before it actually fires). It also prevents me from using setInexactRepeating, which is a useful way to reduce battery use.

How hard was it to add AlarmManager.isSet()? Would it be abused? Well, ok, make sure applications can’t query alarms set by other packages.

If anyone has a better solution, I’d really like to hear it.

Meanwhile, I’m not the only one having difficulties with this. Here is the output of adb dumpsys alarm from my Xperia Arc. About 120 (a hundred and twenty) alarms for something called com.google.android.gsf, and about 20 (twenty) alarms for MyBackup Pro, most of them for same or very close time values.

Advertisements
Categories: android
  1. Ted
    February 19, 2012 at 12:34 am

    As to getting the alarm to fire immediately, I set the time to be an hour ago, and the first alarm goes off approximately immediately.

    This really just pushes the problem into other code, though. In an ideal world you would set the recurring at install, and re-establish by catching the BOOT_COMPLETED broadcast. I wish they would add since tags in the javadocs for constants, this is the first place I’ve seen it mentioned that FIRST_LAUNCH is r12 after I’ve been trying to use it in r8 code, lol

    Right now I’m gonna punt and put a menu option to start my sampling service the first time so I’m not throwing a broadcast every time my Activity is created.

  2. Jon
    August 11, 2011 at 6:30 pm

    Kind of hacky and similar to what you already have, but doesn’t require shared prefs: Can you add an extra on the PendingIntent to indicate the time it should go off? Then, when you use FLAG_NO_CREATE, if you get a PendingIntent back, check its alarm time extra?

    In the broadcast receiver, when all is going well, you would use FLAG_UPDATE_CURRENT on the intent to change that extra, and in this way you’d keep a running knowledge of when the next alarm time should be.

    I had thought about an elaborate circular buffer of request codes, basically, and typed it all up before I realized it just amounted to setting an inexact repeating alarm with a shorter interval in every call to your application’s onCreate, and using the alarm time data associated with the PendingIntent to decide whether to actually perform the action or to just return… which doesn’t have quite the battery saving potential, but does allow you to use setInexactRepeating.

    I suspect part of the reason this issue exists in Android is that it’s a corner case, wherein developers are trying to overcome bad/unexpected user behavior. Doesn’t sound like a common use case (I hope)! 🙂 And I’ve heard it’s pretty common practice for developers to just advise users not to use task killers, or the app may not behave as expected.

    Re: the market broadcast, ACTION_PACKAGE_FIRST_LAUNCH was just added in API 12, which I suspect is what you need for first launch–but that doesn’t help you.

    Now you’ve got me thinking about this, because I can see the same bug in my own code–I just didn’t know it was there until I thought about solving it here.

    • Jon
      August 11, 2011 at 6:31 pm

      And apparently my reply went into the wrong box. Woo.

    • August 11, 2011 at 6:41 pm

      Yes, using an extra is a clever idea.

      As for edge cases – it’s not just task killers. The user can stop an application in system settings, and that removes all alarms as well.

      To me it’s just one of the things that the Android team didn’t do very well, and that’s that, we’re left implementing workarounds.

      Just like with the new screen size based resource qualifiers in Android 3.2 – they’ve been telling everyone for the longest time that it doesn’t matter, you shouldn’t care about it, it’s a bad programming / design practice – and then boom, here it is.

  3. Jon
    August 11, 2011 at 3:01 am

    This solution sounds like it should work for you:

    http://stackoverflow.com/questions/3487875/running-code-on-first-run-of-android-app

    You can try to create the pending intent with FLAG_NO_CREATE, and if it’s null, you need to create it; else the pending intent already exists, which implies the alarm is set… I think.

    • August 11, 2011 at 11:43 am

      Nope, that breaks on Samsung Galaxy S with 2.3+ firmware. Stopping the application (in system settings) keeps the intent (tried this a month or two ago).

      The other solution, with the post-install broadcast from Market, does not help for force stop from system settings, and anyway, is broken in Market 3.0 (there is no broadcast anymore).

  4. Andres Santibanez
    June 23, 2011 at 10:52 pm

    Hi. I have encounter the same issue as you. I think i got it resolved by calling a service from the activity and in the service manage the recurring alarm. My code seems to behave ok. Maybe we can solve this together developing some kind of workaround.

    • June 23, 2011 at 11:06 pm

      My current solution – described above – works well, it’s just that I don’t like it.

      And actually, I have users requesting more flexible scheduling options, where setRepeating() won’t work at all (need variable intervals), so it’s all water under the bridge now.

  5. Johan
    May 25, 2011 at 3:54 pm

    If you have stored the time, why not set/reset it using that information?

    From the documentation:
    “If there is already an alarm for this Intent scheduled (with the equality of two intents being defined by filterEquals(Intent)), then it will be removed and replaced by this one”

    • May 25, 2011 at 4:01 pm

      Storing the time is my workaround, I’d rather not do this at all.

      The documentation quote confirms, again, the issue with just setting the alarm every time the UI runs (just in case), as the time of the next alarm will be pushed out further and futher every time.

      I think this behavior is the reason there are 100+ active alarms set by Google code on my phone (see the output of “adb dumpsys alarm” in the post) – from using a new request code in PendingIntent every time.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s