Android: correct way to use AlarmManager without draining battery - android

I need to do a background check in my app, basically polling various URLs for new stuff. So I've read this is done with the AlarmManager.
The thing is, I don't need the user to be notified as soon as new stuff arrives. Let's say I'm checking every hour, I'm perfectly fine with not doing any check while the phone is sleeping, and resuming the checks when the phone is used. That way the user eventually gets the notification, but the drain is minimal.
So I've tried with setInExactRepeating. I'm starting it at boot time, but also at app startup (mostly for the session after install, and to make sure it's running even if the app gets "force killed"). How do I prevent all these checks to add up? (A new check should only be run if none is running, or the previous one needs to be stopped).

I've found setInexact Repeating coupled with ELAPSED_REALTIME (or RTC) works quite well to achieve very minimal battery drain. Using the flag FLAG_UPDATE_CURRENT on an existing pendingintent (same intent and id) will simply refresh the current alarm schedule if it already exists. Alternatively, add the code suggested by Ralgha to check and see if you already created the pending intent and set the alarm, and then if not, set the pending intent and alarm schedule. Both methods will likely have the same impact on battery (negligible). What will cause significant drain is doing something on a frequent schedule. The fact that you only want to update every hour and not when the screen is off, your app will use barely any battery (assuming it is done with it's processing relatively quickly and cleans up after itself nicely)
http://developer.android.com/reference/android/app/AlarmManager.html#ELAPSED_REALTIME
ELAPSED_REALTIME
This alarm does not wake the device up; if it goes off while the device is asleep, it will not be delivered until the next time the device wakes up.
http://developer.android.com/reference/android/app/AlarmManager.html#setInexactRepeating%28int,%20long,%20long,%20android.app.PendingIntent%29
setInexactRepeating
These alarms are more power-efficient than the strict recurrences supplied by setRepeating(int, long, long, PendingIntent), since the system can adjust alarms' phase to cause them to fire simultaneously, avoiding waking the device from sleep more than necessary.
http://developer.android.com/reference/android/app/PendingIntent.html#FLAG_UPDATE_CURRENT
FLAG_UPDATE_CURRENT
...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.

You can check to see if the alarm is already set before you set it, and you can also use a flag so the alarm won't be delivered until the device is awake.
if ( PendingIntent.getBroadcast( context, 0, updateIntent, PendingIntent.FLAG_NO_CREATE ) == null )
{
pendingIntent = PendingIntent.getBroadcast( context, 0, updateIntent, 0 );
getSystemService( ALARM_SERVICE ).setInexactRepeating( AlarmManager.RTC, System.currentTimeMillis(), AlarmManager.INTERVAL_HOUR, pendingIntent );
}

Related

android - What do I need to keep to persist alarm after reboot?

I am currently developing an android application where I need to setup persistent alarms that will fire on a specific date and time and show a notification. It works well but the system clears all alarms upon rebooting.
From other questions I know that if I create a BroadcastReceiver forBOOT_COMPLETED I can rearm canceled alarms. My question is: What information about those alarms I need to keep in order to rearm them when needed?
Some people say that I need to persist all the Intent extras and the fire datetime in order to recreate the PendingIntent upon rebooting.
Others say that if I only persist the requestCode for the PendingIntent, after reboot I can use this code to get the canceled PendingIntent and rearm the alarm, because when the device reboots the PendingIntent's are just canceled instead of deleted.
Which one is right way to do it?
PendingIntents will not persist after reboot, so to be safe just restart your alarms in the BroadcastReceiver with all the intent extras that you make when you first initialized the alarm, and keep the requestcode the same.

When deleting alarm from AlarmManager, should I also cancel PendingIntent?

I'm trying to delete alarm from AlarmManager. I just called AlarmManager.cancel(), and It seems to work fine. Should I also cancel PendingIntent and why?
PendingIntent p; // prepare a pending intent which matches target alarm's intent.
alarmManager.cancel(p);
p.cancel() // should I do that?
Never use FLAG_CANCEL_CURRENT with PendingIntents that you use when setting alarms. If you want to reschedule the alarm for a different time you don't need any flag at all; just create a duplicate PendingIntent with flags of zero and then use it to set() an alarm: this will implicitly cancel the existing alarm and then set it for the newly-specified time. If you used FLAG_CANCEL_CURRENT when you created the new PendingIntent, though, it breaks the Alarm Manager's ability to recognize that it's "the same" as the now-canceled PendingIntent, and you wind up with the old one hanging around, undeliverable, taking up memory and CPU. I've seen apps with this bug rack up literally hundreds of stale alarms in the system, enough to be a noticeable performance and memory-usage hit.
If you just want to change the extras without actually rescheduling the existing alarm, that is what FLAG_UPDATE_CURRENT is for. If you want to reschedule or cancel an existing alarm, don't use any flags at all.
It is not necessary to do this. But it matters upon your usage.
A PendingIntent itself is simply a reference to a token maintained by
the system describing the original data used to retrieve it. This
means that, even if its owning application's process is killed, the
PendingIntent itself will remain usable from other processes that have
been given it. If the creating application later re-retrieves the same
kind of PendingIntent (same operation, same Intent action, data,
categories, and components, and same flags), it will receive a
PendingIntent representing the same token if that is still valid, and
can thus call cancel() to remove it.
If you only need one PendingIntent active at a time for any of the Intents you will use, then you can alternatively use the flags FLAG_CANCEL_CURRENT or FLAG_UPDATE_CURRENT to either cancel or modify whatever current PendingIntent is associated with the Intent you are supplying.

Do you need to remove an alarm before setting new one

I have an Android alarmManager being set and a broadcastReceiver to pick it up, whenever the app triggers onResume, I am forcing a new alarm to be set.
So I'm wondering for best practices and considering good resource management, should I be removing the first alarm (if it hasn't triggered) before I reset a new alarm ?
Potentially if an alarm doesn't trigger, each time the user re-opens the app, is it creating more resource usage even if the alarm is for the same time ?
Yes you should cancel it when it is appropriate.
I suspect you will have a lot of wasted cycles otherwise. For Example,
App opens at Time T.
Schedule an alarm for T'.
Next the user opens the app again at some time, X seconds prior to T'.
You will then schedule an alarm presumable at another time T''.
However you will get an alarm firing in X secs anyways. If you ignore it, you are simply wasting battery if the phone had to wake in order to deliver the alarm.
If you create the same PendingIntent for your alarm, then the previous one will automatically be canceled when you set it again.
See AlarmManager.set(), PendingIntent.

Android does not execute everything on night

I made a program that creates a Service.
The service connects to a website every 5 minutes, collects one line of data and outputs it to a file.
My problem is that it didn't collect the data every 5 minutes. The data was collected once in around 50-70min. Why? Is there something in android that prevents background services from running properly during nights? What is also strange to me, the 50-70 minute breaks happened only when I was not using my phone.
Times it collected the data:
1:40
1:45
1:50
1:55
2:00
2:05
2:10
2:15
2:20
2:25
2:30 I was awake until this.
3:20
4:15
5:00
6:05
6:55
7:55
8:45
10:20
10:50 Woke up at here.
10:55
Your results are not surprising. Android was designed to swipe out (and back in) processes based overall level of resource as well as other factors.
Modifying your service to become a foreground service will certainly improve your results:
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
Foreground services are basically services guaranteed by Android to be less susceptible to swipe out,
which is what you want.
But even a foreground service will halt its processing when the devices goes to sleep. And, if you feel your polling logic is important enough so to keep the device from sleeping, you should also acquire a wakelock.
Note: Long running wakelocks have a huge impact of on battery usage. Handle with care!
Finally, please consider using an alarm for your task. Alarms are usually considered better option for
long running polling tasks.
Depending of an approach you use to schedule your updates, the OS might shift updates to decrease battery drain and overall burden. Delayed tasks might be postponed if the device falls into deep sleep (happens at night mostly as the device is inactive during long periods of time).
It's done this way to prevent your battery to be drained to fast. If you really want to wake your device every 5 minutes, you can check the answer to this post and use the AlarmManager

Best moment/location for setting an AlarmManager

My application shows content for a site that also has a notification system. I want to show if there are new notifications, and I am using an AlarmManager that calls an IntentService.
My question is: where should I start/register this AlarmManager? I've put it in the onCreate() of my activity just for proof-of-concept (and its working fine, thank you very much :) ), but if you would start that activity twice, you would get multiple alarms.
The only possible solution I've come up with is this, but I don't know if this would be best practice
Start the manager in an onCreate() if the preference "alarm started" is false
Set some variable that it is started in preferences.
Now if the alarm stops for some reason, there's no way to restart it. So, a variation would be:
Always call cancel in the onCreate()
And then always set the Alarm.
This seems like a common pattern: Wanting to periodically get information with an alarm, and not setting that alarm more then once. How should I do this? When, where and how to register the alarm?
Also, continueing on #Zelimir 's comment: can you check if a certain alarm is allready set?
Ideally, the alarm would be set regardless of the activity being started or not of course, but that might be another thing.
For completeness, this is the code I'm currently using to start the alarm:
AlarmManager alMan = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(this, CommentService.class);
PendingIntent penInt = PendingIntent.getService(this, 0, i, 0);
alMan.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
AlarmManager.INTERVAL_FIFTEEN_MINUTES,
penInt);
For even more completeness, the app description / situation.
The app is basically showing blogs (journals if you will) from a certain page. It has activities for adding entry, viewing entries, adding comments, etc. On the 'mother' site there is an option to recieve notifications (like the number you see here on SO too when you get a message). I want to show if there are new messages, and so retrieve them every xx minutes. It would be shown in the notificationbar for now, but it might feed some sort of widget later.
If you need more info: the app is called Androblip and it supports a site called blipfoto.com
When, where and how to register the alarm?
That is impossible to answer in the abstract. It depends entirely upon what the business rules are for your app, which you declined to supply in your question.
If the monitoring is to be happening all the time, a typical pattern is to register the alarm:
in onCreate() of your main activity for the very first run of your app
in a BOOT_COMPLETED BroadcastReceiver, to handle reboots, which wipe the AlarmManager roster
can you check if a certain alarm is allready set?
No, but you can cancel it without issue. Just create an equivalent PendingIntent and call cancel() on the AlarmManager.

Categories

Resources