Where/How to set AlarmManager alarms to avoid duplicates - android

I'm fairly new to Android, so please forgive my naiveté.
The app I am working on needs to wake up every hour and do some background data collection. I'm using AlarmManager to set a repeating alarm that starts the service.
I'm currently setting up the alarm in the MainActivity in the onCreate method. My concern is that if the app is closed and reopened and the onCreate method is called again, it would duplicate the alarms. Am I incorrect in assuming this?
One way I'm thinking about circumventing this to use a boolean in SharedPreferences. Is there a more standard way of approaching this issue?
Thank you!

You can set alarm once avoiding duplicates without any problem.
You can statically set broadcast receiver once the OS finishes booting. Register pending intent via PendingIntent.getService() and configure the AlarmManager in the receiver's onReceive() method.
Please note also that every method PendingIntent.getBroadcast(), PendingIntent.getService(), PendingIntent.getActivity() or PendingIntent.getActivities() has flags parameter. You can request your PendingIntent with PendingIntent.FLAG_NO_CREATE flag set. This means that if the system has identical PendingIntent registered then the null is returned. Otherwise new PendingIntent instance is created. Thus, you can rely on this flag to check if your alarm is already set.
Read more about PendingIntent in my response to the similar problem, please(Usage of PendingIntent.cancel() and AlarmManager.cancel()).
Basically, you can still rely on setting your alarm in launching activity by checking first whether you have PendingIntent registered thanks to the PendingIntent.FLAG_NO_CREATE set.
Hope you now understand how to do it.

In onCreate I would probably just cancel all alarms, then set a new alarm. Psuedo code would be something like
Create pending intent for your alarm
call AlarmManager.cancel with that pending intent http://developer.android.com/reference/android/app/AlarmManager.html#cancel%28android.app.PendingIntent%29
set your alarm with the same pending intent.

Related

CWAC : pass data to WakefulIntentService between alarms when used with AlarmManager

When using WakefulIntentService without alarm, one can call
WakefulIntentService.sendWakefulWork(context, intentOfWork);
to pass data to the service through the intent.
When used with AlarmManager one can call
AlarmListener.scheduleAlarms(AlarmManager mgr, PendingIntent pi, Context ctxt);
to pass data through a PendingIntent.
However, this intent is set at the beginning and will always be the same each time the alarm will goes off. What if we need to update the intent data between 2 alarms ?
We could stop the schedule, update the intent and start the alarm again, but is it the correct way ?
What if we need to update the intent data between 2 alarms ?
Then use the first approach, using sendWakefulWork(). The scheduleAlarms() approach is for simple scenarios.

Cancelling Alarms with PendingIntents

I am trying to cancel an alarm that was set last time my app was run. This alarm has a PendingIntent that was set with PendingIntent.getBroadcast and an inner Intent that contains some variables set by intent.putExtra. My question is this, I know that alarms can be canceled by calling alarmManager.cancel(pendingIntent) where pendingIntent is the same as the one used to set the alarm. But, if the variables placed into the intent are changed will the alarm still be canceled? For example, I set an alarm with intent.putExtra("Joe") where Joe is a contact name. Later my app is closed and when it is re-run I try and cancel the alarm for "Joe" but the user has changed the name of the contact to "Jones". Can I cancel the alarm without knowing the variables I put into the intent?
Thanks!
I think it should cancel the alaram anyway, even though some data is different. The cancel method says:
Any alarm, of any type, whose Intent matches this one (as defined by filterEquals(Intent)), will be canceled.
And filterEquals says:
Determine if two intents are the same for the purposes of intent resolution (filtering). That is, if their action, data, type, class, and categories are the same. This does not compare any extra data included in the intents.
Anyhow, I'd still test it myself.
According to this question (which references the documentation), anything you add using putExtra is not taken into account when checking if an intent is equal to another one.
It shouldn't matter if the extra data is changed.

Do i need to use broadcast with alarm manager?

i'm creating an alarm application, and this is the method to run the alarm :
public void startAlarm(int minuteToStart)
{
Toast.makeText(context, "Alarm Start in " + formatTime(minuteToStart), Toast.LENGTH_SHORT).show();
Calendar cal = Calendar.getInstance();
cal.add(Calendar.MINUTE, minuteToStart);
Intent intent = new Intent(context, AlarmActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, idPendingIntent, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);
}
And it run this activity after given specific time:
public class AlarmActivity extends Activity {
......
}
It works, but i see people are using BroadcastReceiver, am i doing it wrong? should i use BroadcastReceiver too? I've been searching about BroadcastReceiver but i don't get what difference it will make with my application.
Thanks.
In the general case, A--C's answer would be correct.
However, you are using RTC_WAKEUP as the alarm type. The only guarantee that we have with _WAKEUP alarms is if we use a BroadcastReceiver, then Android will ensure that the device will stay awake long enough for us to execute onReceive(). Any other type of PendingIntent -- activity or service -- has no guarantee, and it is very possible for the device to fall back asleep before the startActivity() or startService() actually occurs.
You can use AlarmManager with whatever PendingIntent is capable of (Activity, service, Receiver), though, it is usually used with Receivers - taks executing in the future usually are small and don't need an Activity to run in since the user doesn't need something popping up.
A Receiver isn't an Activity, so it does not have a UI and it has a processing time limit of about 10 seconds, so make sure to be quick. If you require a UI to be shown at a specific time, stick with an Activity, but usually this isn't the case unless it's something like an Alarm Clock app that the user has to see). If you have something like a small behind the scenes operation, go for a Receiver. The Receiver's onReceive() gets a Context passed to it so it can do anything a Context can.
Just keep in mind you will have to change the PendingIntent.getActivity() call to whatever else you decide to use if it's not going to be an Activity.
So it all depends on what you want to do.
You don't have to use a BroadcastReceiver. It's just generally frowned upon (in most cases) to steal focus and launch an Activity from the background without user interaction. There are certainly valid use cases though. If you intend to launch an Activity immediately anyway, doing that directly instead of via BroadcastReceiver is perfectly valid.

There was already an alarm for this Intent scheduled,and I want add another one

I register a broadcastreceiver in AndroidMainfest.xml
And in my app, a function is that User can set a time and at this time the app will send a notification. I get the arguments User set ,and use alarmManager to set a task which will happened at the time user set.
But I find that GOOGLE API said that:
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.
So if I want set two or more task,the Intent will be replaced , and at end I can only get one notification,it's not the result I want.
And then I found the intent was identified by action, data, type, class, and categories,
but I can't change action(the intent's action is the intent-filter's action was registred in the AndroidMainfest.xml ),but at the time that I change the other arguments I can't even receive a broadcast.
I thought there are four ways to solve this problem,but I only made one..
create lots of broadcastreceiver and register these in
AndroidMainfest.xml,and in this way I could change the intent's
action
register the broadcastreceiver in the program ,but I didn't
make it
use service + Timer class ..
To make two intent different without change action.
Any help will be appreciated!!
Intent intent = new Intent("aaa"); //there was a broadcastreceiver's intent-filter "aaa"
intent.putExtra("title", title);
intent.putExtra("table", "计划");
PendingIntent pi = PendingIntent.getBroadcast(Alarm.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager)getSystemService(Alarm.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+time, pi);
The code is in onClickListener{onClick(){}}
Instead of Broadcasting which is not letting you to set repeat alarm you can use the calendar Object and set the alarm at whatever desired time. Once the alarm gets ON, your code will run automatically and as many times you have set the alarm.
You can also take a help of the following tutorial. I am sure it will help you out somehow.
http://blog.mikesir87.io/2013/04/android-creating-an-alarm-with-alarmmanager/

Android Alarm and service trigger

Couple of question on Alarm registration and starting service on trigger.
If an alarm is set at couple of mins ahead of current time and then if phone is made switch off, will the alarm trigger on next phone switch on after the schedule time passed?
How to cancel / update pending intent in service? How to get request code in startCommand() method of service?
Will there be a multiple instances of service created if the alarm is triggered after every 10 seconds?
If "switch off" means full power down and not just "once shortly press power button to turn screen off" the answer is "no"
I think you can't get request code at all. As the documentation on getService states, the requestCode field is "currently not used". You should pass all your data with Intent (third arg of getService).
Will not. See http://developer.android.com/reference/android/content/Context.html#startService(android.content.Intent)
Every call to this method will result in a corresponding call to the target service's onStartCommand(Intent, int, int) method
Store the time of the alarm in SharedPreferences. Then register a receiver for android.intent.action.BOOT_COMPLETED (remembering to add a permission for android.permission.RECEIVE_BOOT_COMPLETED to your manifest), and then in the receiver, which will execute on startup, you can see if the alarm's in SharedPreferences, and if so you can reset it if it hasn't passed yet, or decide what to do if the time has already passed.
See problem with cancel the alarm manager pending intent
No. The service's onCreate will only be called once. Its onStart and onStartCommand will be called each time.
From my own app that I'm developing, if an alarm is set for a time when the phone is turned off, it has been executed on the next phone on/boot. That is without a receiver for BOOT_COMPLETED being present. I am unsure if this is expected behaviour or not, or whether it is consistent over phone variants.
I believe if you wish to have your alarm execute the intent at its specified time, you need to use a getBroadcast PendingIntent with a WakeLock as other variants of PendingIntent do not guarantee that phone will remain awake long-enough before it shuts down again. This is information from another post here by CommonsWare, that I will try to find and link to.
I believe you can remove the pendingintent sent to the alarm manager using, for example, a function like:
public void unregisterEvent(PendingIntent sender) { ((AlarmManager) this.getSystemService(Context.ALARM_SERVICE)).cancel(sender);
}
where the PendingIntent has been created exactly as the original intent you are trying to remove. You can update it by supplying the correct id along with a new PendingIntent when calling AlarmManager again:
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
This is what I use to create/rebuild the PendingIntent:
PendingIntent.getService(this, uniqueIndexToIntent, theIntentItself, PendingIntent.FLAG_UPDATE_CURRENT);
The flag will update the intent if it already exists, or will create a new one otherwise.
I don't think it will. However, I'd recommend having your Service call stopSelf() once it has finished doing its work, so that battery usage is minimised. No need to have it running if it has nothing to do!

Categories

Resources