Start alarm in broadcastreceiver and turn off in service - android

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.

Related

Android AlarmManager why calling BroadcastReceiver?

I'm currently working on an app that handles alarms. I have reach the point where I can set the alarms using the AlarmManager and everything seems to work fine, but.. In all the examples that I found, and even in the Android official docs, I have seen people using a BroadcastReceiver for the PendingIntent, and then, calling an activity or whatever they need when the alarm fires. However, I have try to just pass a simple activity to the PendingIntent for the AlarmManager like this:
Intent intent = new Intent (getApplicationContext(), AlarmActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity (this, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set (AlarmManager.RTC_WAKEUP, timeToTrigger.getTimeInMillis(), pendingIntent);
And after testing alarms in several contexts (app in foreground, app not running, etc..) I found that the activity is always been called correctly.
So, my doubt is as simple as: Why do people use a BroadcastReceiver and in the onReceive method call an activity if you can just simple call the activity directly?
Android frowns on this behaviour. You shouldn't interrupt the user if he is doing something else. This is why you don't want to launch an Activity from AlarmManager. Usually, you launch a BroadcastReceiver, which, if it wants to get the user's attention, will post a Notification. The user can then open the app via the Notification whenever he wants to.
Also, often you just want to perform some background process (like fetching data from a server, or updating some statistics), which doesn't require any user-interaction. In this case you would also launch a BroadcastReceiver or Service and not an Activity.
Starting with Android 10 there are more restrictions about background apps launching activities. Therefore, with current versions of Android it has become more difficult to do this. See https://developer.android.com/guide/components/activities/background-starts
So basically, even though it does work, it is not considered "respecting the user" and it probably won't work in the future.

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.

How do you cancel a specific alarm manager after app is restarted?

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.

how do I avoid having two Alarms set (one by the application and one by boot?)

I have boot receiver class which is setting an alarm, as well as the main activity. Does it automatically identify duplicate alarms? If yes, how does it detect it, else how should I prevent it.
When a device is shut down, all current alarms in the system are removed. That's why all alarm related applications register for a boot receiver.
So there's no need to worry about that specific issue, you should just register all your alarms when your boot receiver is called, and it'll work.
Provided the requestCode used in your alarm PendingIntent is the same the AlarmManager will delete and recreate the alarm and will not duplicate it.
PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);

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