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.
Related
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.
I'm creating an app that, after receiving a text from a certain number, starts a repeating alarm using AlarmManager. The AlarmReciever plays an alarm sound for thirty seconds and then the alarm repeats every five minutes. I want to cancel the AlarmManager when the app is closed and restarted by the user but I have to use the same instance of the alarmIntent to cancel it.
I have to use the same instance of the alarmIntent to cancel it.
No, you have to use an equivalent PendingIntent to cancel it. By "equivalent", I mean:
It is the same operation (e.g., activity, service, broadcast)
It has the same request code (2nd parameter to methods like getActivity())
It has an equivalent Intent
By "equivalent Intent", I mean that all the routing information is the same (component, action, data, MIME type, categories). Extras do not matter.
You need to hold onto enough information in a persistent data store (e.g., file) to be able to create an equivalent PendingIntent to pass to cancel() on AlarmManager.
I'm developing a little Android app, that needs to run a background process, used to start a remote connection periodically (for example, to check if there is new data on the server). This process obviously needs to work also if the application activity is not running at the moment.
As I can see in documentation, there are two types of approach to develop a scheduled background process in Android, working also when the application is closed.
Services
Alarms
The first one is not so good for my requirementes, because it can be killed by OS in case of low memory, so it is useless for me. startForeground() is not so good because I want the process is silent.
Alarm is ok, because it can't be killed by the OS, so it can work indefinitely. But... If I schedule an Intent with the AlarmManager, how can I preserve a reference to the Intent, surviving at application restart?
For example, if I want to cancel, or reschedule the Alarm, I need the reference to the initial Intent to cancel it thorugh the "AlarmManager.cancel(Intent i)" method. But if the application was restarted by the user, how can I obtain a reference to the initial Intent that was used to start the alarm?
Is there another way to stop an alarm if the launching application was restarted?
about alarms, you can cancel using the intent characteristics, so you don't need a reference to the original intent. In any case, the alarms mechanism still need you to run something on a service.
In any case, you missed another possible solution: SyncAdapter. Its purpose is to sync with servers, but you can do whatever you wish in the code, and it's unlikely the OS will kill it, as opposed to the other solutions you've mentioned.
Sadly even now it lacks on documentation and samples, but I think it can fulfill your needs. Here's what I've found
yes, note that cancel is looking for a PendingIntent, not an Intent per se.
so
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, R.string.some_string, new Intent(this, InitialIntent.class), 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
gets you where you want to be.
note that I'm not getString'ing that string, that's becasue I want a unique Id/request code for this intent that I'm not going to screw up copypasting; I reference the same number when creating the alarm in InititalIntent.
This question was just answered, here:
How can I get the context of other activity?
Just cancel the intent you scheduled
Your best bet would be to make use to push notification (via GCM), whenever something new is available on the server. I am working on similar application where data can be pushed from one side (producers) and needs to be pushed to the consumer apps.
You need to look at BroadcastReceiver and GCM specific communication model. Hope this helps
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 );
}
I think I have a design issue, but I will ask you guys the question.
My applications receives a broadcast in xbroadcastreceiver, i check some conditions and if true, I start a service, say zservice, using Alarm Manager with PendingIntent. Now in zservice, everytime when it is triggered by the alarmmanager, I check some other conditions and based upon a specific condition I need to cancel the alarm, I know alarmManager.cancel(pendingIntent);
will cancel the alarm, but my issue is how to get a access to the pendingIntent as it was in the xbroadcastreciever.
I have tried send pendingIntent to service but so far I am not successful so that when I need, I can turn the alarm off.
Hope this logic makes sense.
I know alarmManager.cancel(pendingIntent); will cancel the alram, but my issue is how to get a access to the pendingIntent as it was in the xbroadcastreciever.
Create an Intent with the same routing information (component, action, categories, MIME type) as the original. Create the same type of PendingIntent from this Intent as before (e.g., getService()). Use that PendingIntent with the cancel() call.