pendingAlarmIntent.cancel() or AlarmManager.cancel(pendingAlarmIntent)? - android

I'm just looking at how to cancel an alarm and I came across these two methods. Which one should be used in what situation and why? Are they both the same?
I am currently doing this:
Intent alarmIntent = new Intent(ChangeAlarmActivity.this, AlarmReceiver.class);
PendingIntent pendingAlarmIntent = PendingIntent.getBroadcast(ChangeAlarmActivity.this, (int)alarm.getID(),
alarmIntent, 0);
pendingAlarmIntent.cancel();
How is that different to this below?
Intent alarmIntent = new Intent(ChangeAlarmActivity.this, AlarmReceiver.class);
PendingIntent pendingAlarmIntent = PendingIntent.getBroadcast(ChangeAlarmActivity.this, (int)alarm.getID(),
alarmIntent, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.cancel(pendingAlarmIntent);

Are they both the same?
No.
If you want to cancel an alarm, call cancel() on AlarmManager.
cancel() on PendingIntent probably will appear to have the same effect -- whatever your alarm events were supposed to trigger no longer happens. However, you are then assuming that AlarmManager will detect this and clean things up on its side, which is not guaranteed. Particularly for _WAKEUP alarms, this could result in the device being woken up for no good reason, wasting battery life.
Which one should be used in what situation and why?
I am sure that cancel() on PendingIntent has use cases. I cannot name any concrete ones, as I have never seen it used. Typically when you are using a PendingIntent, any "cancel" semantics are on the use of the PendingIntent (e.g., you cancel() the alarm via AlarmManager, you cancel() the notification via NotificationManager), not on the PendingIntent itself.
One place for cancel() on PendingIntent, therefore, would be some place where you pass off a PendingIntent and there is no "cancel" to revert that, or you explicitly wish to use cancel() as the revert mechanism. For example, if you are creating some sort of plugin mechanism, and the plugin sends a PendingIntent to the host app, the plugin might use cancel() to say "stop using that PendingIntent", which the host app would find out about the next time it tried to send() the PendingIntent and got the exception. Personally, I'm not a huge fan of this -- much along the AlarmManager lines, you don't know what resources might be used by the host app if it fails to properly handle this scenario. But, used properly, it could certainly work.
How is that different to this below?
The "below" one is what I would recommend that you use.

Related

Android: AlarmManager setExact() not firing event in Android 8

I have an Android implementation in which I have to schedule notifications in the long term, I create the PendingIntent and use AlarmManager#setExact() method to do it:
Intent intent = new Intent(mContext, BirthdayReceiver.class);
intent.putExtra(BirthdayReceiver.INTENT_WHO, contact.getName());
PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext, id, intent, 0);
mAlarmManager.setExact(
AlarmManager.RTC,
notificationCalendar.getTimeInMillis(),
pendingIntent);
I've checked the time passed to setExact() method, if it's a short time (minutes, few hours) then the notification appears correctly.
But if I set a notification to fire, let's say the next day at 12:00 it doesn't work.
I am using an Android 8 device to test it.
Any help?
Seems like flag is not specified.
Could you try it with FLAG_UPDATE_CURRENT .
PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Also, in order to confirm if your PendingIntent is still available in the system, you can use FLAG_NO_CREATE.
PendingIntent broadcastIntent = PendingIntent.getBroadcast(mContext, id,
intent, PendingIntent.FLAG_NO_CREATE);
if broadcastIntent returns null then the PendingIntent is not avaialble (possibly cancelled) in the system.
As per the documentation of setexact
This method is like set(int, long, PendingIntent), but does not permit
the OS to adjust the delivery time. The alarm will be delivered as
nearly as possible to the requested trigger time.
So you may not expect the alarm to be triggered immediately.
Note: only alarms for which there is a strong demand for exact-time
delivery (such as an alarm clock ringing at the requested time) should
be scheduled as exact. Applications are strongly discouraged from
using exact alarms unnecessarily as they reduce the OS's ability to
minimize battery use.
So you can really consider whether you really need to call this API or not

Is it bad practice to store PendingIntent's in an object for later retrieval to cancel an Alarm?

I'm making an alarm clock app using PendingIntent and AlarmManager. I know in order to cancel a PendingIntent, you need to recreate it exactly. In my app, much like many other alarm clock apps, there is a list of alarms with a switch to toggle each alarm on/off. When toggled off, I currently recreate the PendingIntent and cancel it using the following:
Intent intent = new Intent(context, MyBroadcastReceiver.class);
String id = this.getId().replaceAll("[^0-9]+", "");
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, id, intent, 0);
alarmIntent.cancel();
The first 3 lines of the code above I believe are unnecessary. When I toggle the alarm, I have access to the custom alarm object which contains the id along with many other details of the alarm. When I'm creating a new alarm, if I store the newly created PendingIntent in the alarm object, I can have access to the PendingIntent used to create the alarm, and just retrieve it and cancel it in 1 line, like this:
this.getPendingIntent().cancel();
I wouldn't have to recreate the intent, get the id, or recreate the PendingIntent from the code shown earlier. This would ultimately save time and resources (not much but it's good practice), so I have a couple questions:
1) Is there anything wrong with storing the PendingIntent in an object and use it later instead of recreating it? It seems like a straightforward answer but I haven't seen anyone do this before.
2) Is there an advantage to recreating the PendingIntent that I'm not aware of?
Thanks!
Is there anything wrong with storing the PendingIntent in an object and use it later instead of recreating it?
Assuming that the underlying Intent doesn't have some massive payload (e.g., Bitmap in an extra), you should be OK.
It seems like a straightforward answer but I haven't seen anyone do this before.
https://github.com/commonsguy/cw-omnibus/tree/v8.7/AlarmManager/Simple, though it's a trivial example.
Is there an advantage to recreating the PendingIntent that I'm not aware of?
It works for cases where you do not have the PendingIntent. Your process does not live forever. If you want to use a cached PendingIntent as an optimization, that's fine, but you need to be in position to create the PendingIntent if it is needed.

How do I check if AlarmManager has an alarm set with FLAG_ONE_SHOT

If I create a PendingIntent with FLAG_ONE_SHOT, a subsequent PendingIntent with FLAG_NO_CREATE returns null.
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context,AlarmService.class);
PendingIntent pi = PendingIntent.getService(context,this.getId(),intent,PendingIntent.FLAG_ON_SHOT);
GregorianCalendar alarmtime = new GregorianCalendar(now.get(GregorianCalendar.YEAR),now.get(GregorianCalendar.MONTH),now.get(GregorianCalendar.DAY_OF_MONTH),0,0);
//Set the alarm
if (Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) {
am.set(AlarmManager.RTC_WAKEUP,alarmtime.getTimeInMillis(), pi);
} else {
am.setExact(AlarmManager.RTC_WAKEUP, alarmtime.getTimeInMillis(), pi);
}
//Now check if the alarm was set, if it was set, the following PendingIntent should return not null but it doesn't
PendingIntent piCheck = PendingIntent.getService(context,this.getId(),intent,PendingIntent.FLAG_NO_CREATE);
if (piCheck!=null) {
Log.d(TAG,"piCheck returned NOT NULL and probably returned pi");
} else if (piCheck==null) {
Log.d(TAG,"piCheck returned NULL pi does not exist");
However if I change the first pending intent to:
PendingIntent pi = PendingIntent.getService(context,this.getId(),intent,PendingIntent.FLAG_CANCEL_CURRENT);
Then my second PendingIntent returns not null as expected.
Both PendingIntents set an alarm properly, but I cannot "check" the FLAG_ONE_SHOT PendingIntent. What is the reason for this behaviour? What is the purpose of it?
I created a small test program to verify this behaviour. If you create a PendingIntent using FLAG_ONE_SHOT and then pass this to the AlarmManager, it looks like Android "consumes" the PendingIntent immediately so that it no longer exists (because it is a "one-shot", it can only be used once). I would have thought this would happen when the alarm actually triggers, but it looks like that isn't the case.
You learn something new every day :-)
To solve your problem, I would just remove the FLAG_ONE_SHOT as you probably don't need it (just pass 0 as the "flags" argument). If you set the alarm more than once, you can use FLAG_UPDATE_CURRENT if you set the alarm with different extras each time (but it looks like you aren't using any extras, so you probably don't need this). I don't think that you need FLAG_ONE_SHOT for what you are trying to do.
The problem here arises because FLAG_ONE_SHOT describes the PendingIntent and so will be needed to identify it.
You can check for its existence by using FLAG_ONE_SHOT | FLAG_NO_CREATE.
Your code snippet is essentially checking for a different PendingIntent (one without FLAG_ONE_SHOT) which doesn't exist and hence you get null.
You can also try this out with FLAG_IMMUTABLE which is another flag that describes the requested PendingIntent.
I don't think that alarms are involved.

Android Dev: AlarmManager Reschedule and Service

please refer to
this for the issue i was facing....as suggested by the person who replied to my problem + what my additional research showed is that i need to use AlarmManager to solve my sleep & timertask issue...since, i have started changing the existing code to utilize PendingIntent along with AlarmManager with: Thread thr = new Thread(null, mTask, "ServiceName"); in the service...
after reading the AlarmManager documentation several times, i dont know how to reschedule a AlarmManager... im working on a Profile Switcher application, which will various run intervals (like i described in my problem thread)...
Does anyone have any suggestion?
i dont know how to reschedule a AlarmManager
Use cancel() to remove an existing alarm, and use set() or any of the other set...() methods to schedule alarms. To "reschedule" alarms, you cancel the old and create the new.
If you use the same intentString and the same request code, your alarms will be overwritten.
In your intent, you can use the following flag:
PendingIntent.FLAG_UPDATE_CURRENT
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, notification.getId(), new Intent(intentString), PendingIntent.FLAG_UPDATE_CURRENT);
long startDateTimeMillis = notification.getDateTime().toDateTime().getMillis();
MyApplication.alarmManager().setExact(AlarmManager.RTC_WAKEUP,
startDateTimeMillis,
pendingIntent);
Related to the alarm itself, you can use the methods setExact() or set() and you can search about them to check which one fits you better.

PendingIntent in Widget + TaskKiller

I've developed an Application (called Instant Buttons) and the app has a widget feature. This widget uses PendingIntent for the onClick of the widget.
My PendingIntent code is something like this:
Intent active = new Intent(context, InstantWidget.class);
active.setAction(String.valueOf(appWidgetId));
active.putExtra("blabla", blabla); //Some data
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
actionPendingIntent.cancel();
actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
remoteViews.setOnClickPendingIntent(R.id.button, actionPendingIntent);
The onReceive gets the intent and do some stuff with the MediaPlayer class to reproduce a sound.
I have reports from some users that the widgets stop working after a while and with some research i've discovered is because the Task Killers. It seems that when you kill the app in the TaskKiller, the PendingIntent is erased from memory, so when you click the widget, it doesn't know what to do.
Is there any solution for this? Is my code wrong or something or it's the default behavior of the PendingIntent? Is there something I can use to avoid the TaskKiller to stop my widgets from working??
Greetings.
Is there any solution for this?
Ask your users not to use task killers. Or, wait for some future Android release to close the task-killer loophole.
Is my code wrong or something or it's
the default behavior of the
PendingIntent?
Your code is presumably fine. The hack the task-killers use wipes out pretty much everything, so I'm not the least bit surprised at this behavior.
Is there something I can use to avoid
the TaskKiller to stop my widgets from
working?
Not really. Your users -- those who aren't total morons, at least -- will hopefully learn to be more aware of the impacts of their use of task-killers. At present, there is no sensible defense against a task killer app.
I had this same problem with pending intents in a widget no longer connecting to their activities when the app had been dismissed. Creating, canceling, and recreating worked, but you can also specify a cancel current flag so that you do not have to explicitly call cancel yourself as the system will do it for you.
When you are creating your pending intent instead of using
PendingIntent.getBroadcast(context, 0, active, 0);
use
PendingIntent.getBroadcast(context, 0, active, PendingIntent.FLAG_CANCEL_CURRENT);

Categories

Resources