My code
public class BackgroundIntentService extends IntentService {
public BackgroundIntentService() {
super("BackgroundIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
scheduleNextUpdate();
Log.w("Blabla", "asldad111");
Log.w("Blabla", "asldad");
Log.w("Blabla", "asldad");
Log.w("Blabla", "asldad555");
}
private void scheduleNextUpdate() {
Intent intent = new Intent(this, this.getClass());
PendingIntent pendingIntent =
PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// The update frequency should often be user configurable. This is not.
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 5000, pendingIntent);
}
}
To start the service
Intent serviceIntent = new Intent(this, BackgroundIntentService.class);
startService(serviceIntent);
In the MainActivity.
The problem is that I can see in the logcat those logs spamming, not every 5 seconds but twice a second or more.
Where I'm wrong?
The flag you are using PendingIntent.FLAG_UPDATE_CURRENT in
PendingIntent pendingIntent =
PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
should be PendingIntent.FLAG_CANCEL_CURRENT
as quoted on the developer docs
FLAG_CANCEL_CURRENT - Flag indicating that if the described PendingIntent already exists, the current one should be canceled before generating a new one.
FLAG_UPDATE_CURRENT - Flag indicating that if the described PendingIntent already exists, then keep it but replace its extra data with what is in this new Intent.
so in your case the current pending intent exists and the new one updates it and fires at the exact moment it is updating the existing pending since you have defined the trigger time to be at System.currentTimeMillis()
So what is happening is that the current Pending intent is firing up before the new pending intent updates it ..and once it does that works as per the alarm logic , after the 5000ms interval the pending intent is fired. So there is a race condition here with interleaved alarm triggers and updates via the pending intents.
Related
I have a service which uses PendinIntent and AlarmManager to launch another activity after a fixed period of time.
Here is the relevant code of the service:
Calendar cal = Calendar.getInstance(); //Create a calendar
cal.add(Calendar.MINUTE, 1); //Add the set minutes to the alarm
Intent dialogIntent = new Intent(this, alarmRingLayout.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 1234, dialogIntent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);
When the service starts, I also set up a notification which has the a button that can cancel the service incase the user does not want the activity to launch.
On click of the "cancel" button, stopService() is called:
stopService(new Intent(StopPowerNapAlarmService.this, PowerNapAlarmService.class));
onDestroy() has the following code which cancels the notification and calls stopSelf()
It also tries to cancel the PendingIntent and AlarmManager.
The problem is that the Activity opens up even after after onDestroy is called. I believe the PendingIntent and/or AlarmManager are not getting canceled.
Here is the code for onDestroy():
#Override
public void onDestroy() {
Toast.makeText(this, "Alarm Finished", Toast.LENGTH_SHORT).show();
CancelNotification(this, 0);
//Cancel the pending intent and AlarmManager
Intent myntent = new Intent(PowerNapAlarmService.this, PowerNapAlarmService.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this,
1234, myntent, PendingIntent.FLAG_UPDATE_CURRENT);
pendingIntent.cancel();
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.cancel(pendingIntent);
stopSelf();
}
What is going wrong over here?
What is going wrong over here?
Your Intent objects are different, so your PendingIntent objects are different. This, in turn, means that you are working with different alarms. The first Intent points to an alarmRingLayout.class activity. The second Intent points to a BroadcastReceiver oddly named PowerNapAlarmService.
If you want to cancel() the alarmRingLayout alarm, create an activity PendingIntent for alarmRingLayout, and use that with cancel().
Also, please get rid of stopSelf() in onDestroy(), as that is not needed and could conceivably cause problems.
I am trying to implement an alarm that would display a notification everyday at the same hour of the day.
Here is the function I'm calling in my activity:
private void restartNotify() {
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// Intent for our BroadcastReceiver
Intent intent = new Intent(this, AlarmReceiver.class);
// PendingIntent for AlarmManager
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT );
// In case we have already set up AlarmManager, we cancel.
am.cancel(pendingIntent);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+10000, pendingIntent);
}
And here is my broadcast receiver class
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(R.drawable.icon_notif, context.getString(R.string.NotificationLaunchMssg), System.currentTimeMillis());
// This is intent we want to launch when user clicks on the notification.
Intent intentTL = new Intent(context, MyClass.class);
notification.setLatestEventInfo(context, context.getString(R.string.NotificationTitle), context.getString(R.string.NotificationBody),
PendingIntent.getActivity(context, 0, intentTL, PendingIntent.FLAG_CANCEL_CURRENT));
nm.notify(1, notification);
//Here we set next notification, in day interval
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+10000, pendingIntent);
}
}
As you can see in this code I am using a test value (+10000 milliseconds) because I am simply trying to trigger the alarm 10 seconds after my app has started. But it doesn't work, nothing is displayed.
I don't know if the alarm has a problem, or the notification, nothing is happening.
Do you have any idea why?
Thanks for your help
EDIT: after adding some test code in AlarmReceiver method, it turns out this code is never run. So I probably don't call it properly, what is wrong?
Do not use this approach try setInexactRepeating(...) or setRepeating(...) instead. Why are u giving extra work to the BroadcastReceiver for setting alarm every time it receives the intent.
here is a little code:
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
0, 10000, pendingIntent);
// The pending intent will the same as yours. 10000 is the
// interval for between consecutive alarms
as azertiti mentioned in comments " By the time it's registered that time will already be in the past." so use 0 or System.currentTimeMillis().
I am making an application that uses AlarmManager.setInexactRepeating() method, that takes a PendingIntent as a paramater.
I start this by calling my setAlarm() method
public void setRepeatingAlarm() {
Intent intent = new Intent(this, AlarmReceiver.class);
String url = getAssembledUrl();
intent.putExtra("url", url);
pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
}
and stop it by stopAlarm()
private void stopRepeatingAlarm() {
alarmManager.cancel(pendingIntent);
pendingIntent.cancel();
}
Works just fine. However when activity gets destroyed, and user decides to stop the alarm, obviously the pending intent is null, as it gets created in the setRepeatingAlarm() method that wasnt called during current activities life.
Whats the correct way to get around this?
I could be creating the pending intent in Activity.onCreate() and that would solve this problem, however I will not be able to start the alarm again as the pending intent got canceled and needs to be recreated again (i think, unless there is a way to check the intent was canceled that i dont know about)
Actually, as it turns out
PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
returns the same pending intent, if the intent is the same so, all i needed was
private void stopRepeatingAlarm() {
if(pendingIntent == null) {
pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
alarmManager.cancel(pendingIntent);
}
The code snippet below....
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
///////////Do something////////////////////////
showtext.startScan();
//SEt Alarm
Intent intent = new Intent(this, TextReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+9000, pi);}
And my Receiver :
TextReceiver extends BroadcastReceiver{
public void onReceive(Context context, Intent intent) {
///Show text/////
}
}
The thing is that when I run the program after 9sn, I am getting an error that "The app stopped unexpectedly". Why I get this error?
My goal is to show the text every 9sn. What is the correct usage of AlarmManager in the main activity
OR Should I set alarm in the BroadcastReceiver ?
Which one does makes sense: am.setRepeating or am.set in terms of my goal?
**Edit: How can I change my alarm code to run in the Broadcast Receiver ? **
//try this
AlarmManager am=(AlarmManager)getApplicationContext getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),(9 * 1000), pendingIntent);
Never, ever use FLAG_CANCEL_CURRENT with PendingIntents that are set as alarms.
What happens is that you wind up canceling the validity of the PendingIntent currently held by the alarm manager, and this means that the alarm manager can no longer tell that any newly-set alarm matches that old PendingIntent. You wind up with the old (invalid) alarm still registered along with your new one. If you keep doing this you can wind up with hundreds (or more!) stale alarms registered in the system, none of which will actually do anything but which are taking up memory and CPU.
I have pending intent that sets alarm and it takes parameters such as row id and time from a database. I want to cancel alarm so i would do that by sending another pending intent with the same info and then cancel(i want to cancel it from different file). I only allow one alarm to be set at anyone time because that's the way my app works, because there is only one alarm set from that pending intent is there anyway i can just do cancel all for that intent?
Since I believe in code samples to get the point across, see below:
/*
* An alarm can invoke a broadcast request
* starting at a specified time and at
* regular intervals.
*/
public void sendRepeatingAlarm()
{
Calendar cal = Utils.getTimeAfterInSecs(30);
String s = Utils.getDateTimeString(cal);
this.mReportTo.reportBack(tag, "Schdeduling Repeating alarm in 5 sec interval starting at: " + s);
//Get an intent to invoke TestReceiver class
Intent intent = new Intent(this, TestReceiver.class);
intent.putExtra("message", "Repeating Alarm");
PendingIntent pi = this.getDistinctPendingIntent(intent, 2);
// Schedule the alarm!
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP,
cal.getTimeInMillis(),
5*1000, //5 secs
pi);
}
protected PendingIntent getDistinctPendingIntent(Intent intent, int requestId)
{
PendingIntent pi =
PendingIntent.getBroadcast(
this, //context
requestId, //request id
intent, //intent to be delivered
0);
//pending intent flags
//PendingIntent.FLAG_ONE_SHOT);
return pi;
}
/*
* An alarm can be stopped by canceling the intent.
* You will need to have a copy of the intent
* to cancel it.
*
* The intent needs to have the same signature
* and request id.
*/
public void cancelRepeatingAlarm()
{
//Get an intent to invoke TestReceiver class
Intent intent = new Intent(this, TestReceiver.class);
//To cancel, extra is not necessary to be filled in
//intent.putExtra("message", "Repeating Alarm");
PendingIntent pi = this.getDistinctPendingIntent(intent, 2);
// Schedule the alarm!
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.cancel(pi);
}
I have pending intent that sets alarm and it takes parameters such as row id and time from a database.
There are no "parameters" on a PendingIntent. I am going to interpret this as meaning "extras".
I want to cancel alarm so i would do that by sending another pending intent with the same info and then cancel(i want to cancel it from different file).
It's not "sending" but "creating". Otherwise, yes, this is correct.
i can just do cancel all for that intent?
There is only one alarm.
Extras do not matter in terms of alarm scheduling. If you have an Intent (I1) wrapped in a PendingIntent (PI1) and use that to schedule an alarm, and later if you create an Intent (I2) with the same component/action/data/type, wrap that in a PendingIntent (PI2) and cancel() the alarm, it will cancel the PI1 alarm. Similarly, if you use PI2 to schedule a new alarm, it will remove the old PI1 alarm.