stuck around into a problem with
AlarmManager
I developed a method which adds two alarms into an AlarmManager Array. My method given below runs on a button click and adds as many as 10 alarms in the AlarmManager Array two per click.
My method code is given below.
public void stupidAlarm()
{
stupidPendingIntentOne = PendingIntent.getBroadcast(context, listItemClickedPosition, stupidIntentOne, PendingIntent.FLAG_UPDATE_CURRENT);
stupidPendingIntentTwo = PendingIntent.getBroadcast(context, listItemClickedPosition+5, stupidIntentTwo, PendingIntent.FLAG_UPDATE_CURRENT);
stupidAlarm[listItemClickedPosition]= (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
stupidAlarm[listItemClickedPosition+5]= (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmOneTime = settings.getString(AlarmOneTime, null);
alarmTwoTime = settings.getString(AlarmTwoTime, null);
try
{
OneHr = Integer.parseInt(muteTime.substring(0, 2));
OneMin = Integer.parseInt(muteTime.substring(2, 4));
TwoHr = Integer.parseInt(ringerTime.substring(0, 2));
TwoMin = Integer.parseInt(ringerTime.substring(2, 4));
}
catch(Exception ex)
{
Toast.makeText(context, ex.getMessage(), Toast.LENGTH_SHORT).show();
}
stupidCalOne.set(2015, Calendar.MAY, 2);
stupidCalOne.set(Calendar.HOUR_OF_DAY, OneHr);
stupidCalOne.set(Calendar.MINUTE, OneMin);
stupidCalOne.set(Calendar.SECOND, 0);
stupidCalOne.set(Calendar.MILLISECOND, 0);
stupidCalTwo.set(2015, Calendar.MAY, 2);
stupidCalTwo.set(Calendar.HOUR_OF_DAY, TwoHr);
stupidCalTwo.set(Calendar.MINUTE, TwoMin);
stupidCalTwo.set(Calendar.SECOND, 0);
stupidCalTwo.set(Calendar.MILLISECOND, 0);
stupidAlarm[listItemClickedPosition].set(AlarmManager.RTC_WAKEUP, stupidCalOne.getTimeInMillis(), stupidPendingIntentOne);
stupidAlarm[listItemClickedPosition+5].set(AlarmManager.RTC_WAKEUP, stupidCalTwo.getTimeInMillis(), stupidPendingIntentTwo);
}
But the problem is out of 10 Alarms created some works and some does not!
following information may be helpful in this context
AlarmOne 1047Hrs works
AlarmTwo 1048Hrs works
AlarmThree 1049Hrs does not work
AlarmFour 1050Hrs works twice
AlarmFive 1051Hrs does not work
AlarmSix 1052Hrs does not work
AlarmSeven 1053Hrs works thrice
AlarmEight 1054Hrs works
AlarmNine 1055Hrs does not work
AlarmTen 1056Hrs works twice
My declaration of Calendar, Intent, PendingIntent and AlarmManagerArray
//for stupid alarm
public Calendar stupidCalOne;
public Calendar stupidCalTwo;
public Intent stupidIntentOne;
public Intent stupidIntentTwo;
public PendingIntent stupidPendingIntentOne;
public PendingIntent stupidPendingIntentTwo;
public AlarmManager[] stupidAlarm;
My assignments in onCreate method
//for stupid alarm
stupidCalOne = new GregorianCalendar();
stupidCalTwo = new GregorianCalendar();
stupidIntentOne = new Intent(context, OneAlarmReceiver.class);
stupidIntentTwo = new Intent(context, TwoAlarmReceiver.class);
stupidAlarm = new AlarmManager[10];
Any help will be highly appreciated, thanks in advance
The OS is allowed to batch Alarms.
The docs for AlarmManager.set state:
Note: Beginning in API 19, the trigger time passed to this method is treated as inexact: the alarm will not be delivered before this time, but may be deferred and delivered some time later. The OS will use this policy in order to "batch" alarms together across the entire system, minimizing the number of times the device needs to "wake up" and minimizing battery use. In general, alarms scheduled in the near future will not be deferred as long as alarms scheduled far in the future.
And that is why some times are skipped and then fire a minute later along with another alarm.
I suspect the further apart they are, the less likely the system would be to batch them, but you still can't guarantee alarm time with this.
It goes on:
With the new batching policy, delivery ordering guarantees are not as strong as they were previously. If the application sets multiple alarms, it is possible that these alarms' actual delivery ordering may not match the order of their requested delivery times. If your application has strong ordering requirements there are other APIs that you can use to get the necessary behavior; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent).
So use setWindow or setExact. but note with setExact:
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.
Related
I want to trigger a notification each day at a specific time choosen by the user, like 6' / 7' / 8'.
For this, I created a WakefulBroadcastReceiver, that pass to an IntentService to create the notification.
And this is how I setup my AlarmsManager. timeInHours is an integer passed as parameter, between 6 and 12 :
Intent i = new Intent(context, StepCountNotifyBroadcast.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
// Get the next day at timeInHours hours'.
Calendar cal = Calendar.getInstance();
cal.setTime(new Date()); // compute start of the day for the timestamp
cal.set(Calendar.HOUR_OF_DAY, timeInHours);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.AM_PM, Calendar.AM);
if (new Date().getTime() > cal.getTime().getTime())
cal.add(Calendar.DAY_OF_YEAR, 1);
long nextDay = cal.getTime().getTime();
// Setup the alarm.
long timeBetween = AlarmManager.INTERVAL_DAY; // Each day.
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarms.cancel(pi);
alarms.setRepeating(AlarmManager.RTC_WAKEUP, nextDay, timeBetween, pi);
Date nextDayAsDate = new Date(nextDay);
GLog.d("AlarmUtils", "scheduleNotifiation for next date: " + nextDayAsDate.toString());
It works well 50% of the time, but still do crazy things like... Trigger the notification at 2:00 AM ?!
I know the system will not trigger the notification at the specific time I want and this is ok. But here we talk about approximately 18 hours later.
In logs, it seems that my IntentService code is effectively running in the middle of the night in some cases. So that's not an issue with the notification itself.
I can switch this line :
alarms.setRepeating(AlarmManager.RTC_WAKEUP, nextDay, timeBetween, pi);
to another line of code :
alarms.setExact(AlarmManager.RTC_WAKEUP, nextDay, pi);
But this is not good for the battery life I'm afraid.
Now my questions :
Can somebody tell me a reason about the way Android devices works with AlarmsManager ?
Do you find another solution than will not drain battery life and be sure the notification will be triggered in maximum 2 or 3 hours later?
Do you have any ideas, in this particular case, to debug and do code testing without waiting a day or two to detect issues?
Thanks in advance.
After API Level 23, Doze mode was introduced on the Android System to reduce battery consumption. Go through below link:
How to make Alarm Manager work when Android 6.0 in Doze mode?
I'm using android studio to develop an application that would retrieve the time information from the database and would set an alarm to prompt the user that it's time to drink the medicine, my problem is that the alarm goes on too early
for example the retrieved time is 12:50 AM and the current time is 12:40 AM
even though there's a 10 minutes difference the alarm goes off right away
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm");
try {
Calendar Cnow = Calendar.getInstance();
long now=Cnow.getTimeInMillis();
Calendar time=Calendar.getInstance();
time.setTimeInMillis(0);
Date dTime=sdf.parse(strAlarmDate + " " + strTime1);
//example strAlarmDate="2015-09-27" and strTime1="12:50 AM" and now="12:40 AM" in millis
//dTime="...... 2015-09-27 00:50"
time.setTime(dTime);
//Date dNow=now.getTime();
//time.setTime(date);
//long trigger=time.getTimeInMillis()-now.getTimeInMillis();
long trigger=time.getTimeInMillis()-now;
if(trigger>0) {
ctr++;
Intent i = new Intent(Login.this, AlarmReceiver.class);
i.putExtra("message", strMedname);
i.putExtra("line", strLine);
final int _id = (int) System.currentTimeMillis();
PendingIntent pi = PendingIntent.getActivity(Login.this, _id, i, 0);
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
// am.set(AlarmManager.RTC_WAKEUP, trigger, pi);
// am.setExact(AlarmManager.RTC_WAKEUP, trigger, pi);
am.set(AlarmManager.RTC_WAKEUP, trigger, pi);
}
}catch(ParseException ex)
{
ex.printStackTrace();
}
The problem is that you set the alarm to go off at time trigger which is the time minus the current time.
You should pass time.getTimeInMillis() instead.
So replace the line with
am.set(AlarmManager.RTC_WAKEUP, time.getTimeInMillis(), pi);
}
Bas van Stein is correct. AlarmManager uses an absolute time: milliseconds since epoch, January 1, 1970. You are attempting to set it to a relative time: milliseconds from the time you are setting the alarm. AlarmManager interprets that as being some time very close to epoch, around January 1, 1970, at 12:10AM. Since that time is long since past, the alarm triggers immediately.
The solution is simply to use time.getTimeInMillis() in set instead of trigger.
Some other notes for doing this kind of work, since your app is similar in intention to mine:
Most alarms since API 19 will be inexact, as Frank N. Stein pointed out. The degree of this inexactness depends on implementation and also how far in the future the alarm is created. For something like medicine which is probably at least daily, in all of my testing the alarm has fired within a minute of the intended time.
You are using the current time as the requestCode in your PendingIntent. By setting this to a number that is meaningful to your program and storing that value, you can later update or cancel this alarm by recreating the same PendingIntent.
From API level 23 and forward, urgent alarms like medication alarms should use setAndAllowWhileIdle() to ensure that their alarm goes off during low power idle states. I do not yet know if using set with RTC_WAKEUP will be sufficient to ensure this behavior.
From the official docs:
Note: Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups and battery use. There are new APIs to support applications which need strict delivery guarantees; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent). Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested.
I am using alarm manager (setInexactRepeating) but it fires events after an interval of 1 min.Is their any way I could set this interval to be 5 seconds.
alarmIntent = new Intent(this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(c, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
//Alarm Interval
int interval = 5000;
//Firing alarm after an interval of 5 seconds
manager.setInexactRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(), interval, pendingIntent);
Create a private Handler variable in your Activity
private Handler mHandler = new Handler();
Then create a Runnable
private Runnable myTask = new Runnable() {
public void run() {
doMyThing();
mHandler.postDelayed(this, 5000); // Running this thread again after 5000 milliseconds }
};
To start running the task every 5 seconds
mHandler.postDelayed(myTask, 0);
Then to stop running the task
mHandler.removeCallbacks(myTask);
This way myTask will be executed every 5 seconds without having to keep a loop running constantly.
EDIT:
You cannot have exact intervals with repeating alarms starting from API level 19. If you want to use AlarmManager, and your application's targetSdkVersion is 19 or higher, use the setExact() method as described below.
public void setExact (int type, long triggerAtMillis, PendingIntent operation)
Added in API level 19
Schedule an alarm to be delivered precisely at the stated time.
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.
I think that waking up the CPU in 5 second intervals indefinitely is just too expensive in terms of battery usage.
See this
Note: Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups and battery use. There are new APIs to support applications which need strict delivery guarantees; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent). Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested.
And this
Note: as of API 19, all repeating alarms are inexact. If your application needs precise delivery times then it must use one-time exact alarms, rescheduling each time as described above. Legacy applications whose targetSdkVersion is earlier than API 19 will continue to have all of their alarms, including repeating alarms, treated as exact.
The last quote means that the OS will always change the interval in repeating alarms, in API level 19 and above. So you won't be able to get exactly 5 seconds with repeating alarms in newer platforms.
So the best solution is to use setExact() and reschedule every time your code is executed.
Or if you want to use repeating alarm with exact interval, use setRepeating() while setting targetSdkVersion below 19 like in the quoted text above.
There a many was of executing a background task at an interval.
The simplest way is to create a new thread using the Runnable object.
Look at the following code:
Runnable newThread = new Runnable() {
#Override
public void run() {
while (true) {
Log.d("Msg from BG Thread", "This message will be displayed every 5 seconds");
try {
Thread.sleep(5000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
};
newThread.run();
Let me know if this solution works for you.
After a year, I think I'm just going to post another answer using AlarmManager. So here it is. You can set your targetSdkVersion as high as you want, and it'll work. No Services required!
public class AlarmReceiver extends BroadcastReceiver {
int interval = 60000; // 1 minute
#Override
public void onReceive(Context context, Intent intent) {
doYourThing();
Intent i = new Intent(context, AlarmReceiver.class);
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
// You can doYourThing() again after an interval
am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+interval, PendingIntent.getBroadcast(context, 1, i, PendingIntent.FLAG_UPDATE_CURRENT));
}
}
}
Please help me. Why does AlarmManager not exactly repeats an event when the repetition period of more than one day?
Here is my code to run AlarmManager. The variable time_period contains the following value 60*1000*60*24*7 which is equal to 7 days. In the end, if I change the date on your phone, the event is triggered by 6-4-7-7-7-7 days.
This can be seen in the logs of the application (See link http://prntscr.com/7kdqbw ) Thanks in advance for your reply.
Intent notification = new Intent(this , ServiceReminders.class);
notification.putExtra("backup", "backup");
AlarmManager alarmManagerBackup = (AlarmManager)getSystemService(ALARM_SERVICE);
PendingIntent pibackup = PendingIntent.getService(this, 3, notification, 10);
if (sdkVersion < 19) {
alarmManagerBackup.setRepeating(AlarmManager.RTC_WAKEUP, 0, time_period, pibackup);
}
else if (sdkVersion >= 19) {
alarmManagerBackup.setInexactRepeating(AlarmManager.RTC_WAKEUP, 0, time_period, pibackup);
}
Log.d("ServiceManagerNotification", "AlarmBackup sdkVersion = "+sdkVersion);
You are explicitly leveraging the inexact alarms by calling setInexactRepeating(). Unsurprisingly, this results in your alarms being set at inexact intervals.
The problem is further compounded by the fact that you are supplying a custom period. If you do not use one of the pre-defined intervals, then the framework will simply call setRepeating() using your interval instead of using setInexactRepeating().
From the documentation for the intervalMillis parameter:
interval in milliseconds between subsequent repeats of the alarm. Prior to API 19, if this is one of INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_HALF_DAY, or INTERVAL_DAY then the alarm will be phase-aligned with other alarms to reduce the number of wakeups. Otherwise, the alarm will be set as though the application had called setRepeating(int, long, long, PendingIntent). As of API 19, all repeating alarms will be inexact and subject to batching with other alarms regardless of their stated repeat interval.
In the end, regardless of how you set this alarm, it will be inexact if you set it as a repeating alarm and there isn't a whole lot you can do about it.
If you need more precision, you should use either setWindow() or setExact() and set the next alarm every time your alarm triggers. Although if you are only performing an action once a week, it is likely that in the end you don't need that precision.
Can someone explain to me the difference between AlarmManager.RTC_WAKEUP and AlarmManager.ELAPSED_REALTIME_WAKEUP? I have read the documentation but still don't really understand the implication of using one over the other.
Example code:
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
scheduledAlarmTime,
pendingIntent);
alarmManager.set(AlarmManager.RTC_WAKEUP,
scheduledAlarmTime,
pendingIntent);
How different will the two lines of code execute? When will those two lines of code execute relative to each other?
I appreciate your help.
AlarmManager.ELAPSED_REALTIME_WAKEUP type is used to trigger the alarm since boot time:
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 600000, pendingIntent);
will actually make the alarm go off 10 min after the device boots.
There is a timer that starts running when the device boots up to measure the uptime of the device and this is the type that triggers your alarm according to the uptime of the device.
Whereas, AlarmManager.RTC_WAKEUP will trigger the alarm according to the time of the clock. For example if you do:
long thirtySecondsFromNow = System.currentTimeMillis() + 30 * 1000;
alarmManager.set(AlarmManager.RTC_WAKEUP, thirtySecondsFromNow , pendingIntent);
this, on the other hand, will trigger the alarm 30 seconds from now.
AlarmManager.ELAPSED_REALTIME_WAKEUP type is rarely used compared to AlarmManager.RTC_WAKEUP.
Despite the currently accepted and up-voted answer, AlarmManager.ELAPSED_REALTIME* types along with SystemClock.elapsedRealtime() has always been more reliable than the RTC clocks for alarms and timing.
Using ELAPSED_REALTIME_WAKEUP with AlarmManager will rely on a monotonic clock starting from boot time "and continues to tick even when the CPU is in power saving modes, so is the recommend basis for general purpose interval timing". So,
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()
+ 60*1000, pendingIntent);
will make your PendingIntent fire in 1 min (60*1000 milliseconds).
Whereas, AlarmManager.RTC_WAKEUP is for the the standard "wall" time in milliseconds since the epoch. So,
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
+ 60*10000, pendingIntent);
may also trigger the alarm 60 seconds from now, but not reliably, because as noted in the SystemClock documentation:
The wall clock can be set by the user or the phone network (see
setCurrentTimeMillis(long)), so the time may jump backwards or
forwards unpredictably. This clock should only be used when
correspondence with real-world dates and times is important, such as
in a calendar or alarm clock application. Interval or elapsed time
measurements should use a different clock. If you are using
System.currentTimeMillis(), consider listening to the
ACTION_TIME_TICK, ACTION_TIME_CHANGED and ACTION_TIMEZONE_CHANGED
Intent broadcasts to find out when the time changes.
Also, the question only referenced only the *_WAKEUP alarms but see also the AlarmManager documentation on that to make sure you understand what the wakeup vs non-wakeup alarms provide.
Just a note. You can get the uptime millis calling:
long uptimeMillis = SystemClock.elapsedRealtime();
So if you want to fire the alarm 30 seconds from now, and you want to use the uptime clock instead of the normal clock, you can do:
long thirtySecondsFromNow = SystemClock.elapsedRealtime() + 30 * 1000;
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, thirtySecondsFromNow, pendingIntent);
Whenever you want to check for some elapsed time instead of a specific date/time, it's best to use the uptime. That's because the current time set by the user in the device can change if the user changes it using the settings.
I programmed this problem in my own project this way. in below code i am using
AlarmManager.ELAPSED_REALTIME_WAKEUP
to set alarm at a specific time.
the variable 'intentName' is used in the intentFilter to receiver this alarm. because i am firing many alarms of this type. when i cancel all alarms. i use the method cancel. given at bottom.
//to hold alarms and cancel when needed
public static ArrayList<String> alarmIntens = new ArrayList<String>();
//
public static String setAlarm(int hour, int minutes, long repeatInterval,
final Context c) {
/*
* to use elapsed realTime monotonic clock, and fire alarm at a specific time
* we need to know the span between current time and the time of alarm.
* then we can add this span to 'elapsedRealTime' to fire the alarm at that time
* this way we can get alarms even when device is in sleep mood
*/
Time nowTime = new Time();
nowTime.setToNow();
Time startTime = new Time(nowTime);
startTime.hour = hour;
startTime.minute = minutes;
//get the span from current time to alarm time 'startTime'
long spanToStart = TimeUtils.spanInMillis(nowTime, startTime);
//
intentName = "AlarmBroadcast_" + nowTime.toString();
Intent intent = new Intent(intentName);
alarmIntens.add(intentName);
PendingIntent pi = PendingIntent.getBroadcast(c, alarms++, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
//
AlarmManager am = (AlarmManager) c
.getSystemService(Context.ALARM_SERVICE);
//adding span to elapsedRealTime
long elapsedRealTime = SystemClock.elapsedRealtime();
Time t1 = new Time();
t1.set(elapsedRealTime);
t1.second=0;//cut inexact timings, seconds etc
elapsedRealTime = t1.toMillis(true);
if (!(repeatInterval == -1))
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
elapsedRealTime + spanToStart, repeatInterval, pi);
else
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, elapsedRealTime
+ spanToStart, pi);
where span function is this:
public static long spanInMillis(Time startTime, Time endTime) {
long diff = endTime.toMillis(true) - startTime.toMillis(true);
if (diff >= 0)
return diff;
else
return AlarmManager.INTERVAL_DAY - Math.abs(diff);
}
alarm cancel function is this.
public static void cancel(Context c) {
AlarmManager am = (AlarmManager) c
.getSystemService(Context.ALARM_SERVICE);
// cancel all alarms
for (Iterator<String> iterator = alarmIntens.iterator(); iterator
.hasNext();) {
String intentName = (String) iterator.next();
// cancel
Intent intent = new Intent(intentName);
PendingIntent pi = PendingIntent.getBroadcast(c, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
am.cancel(pi);
//
iterator.remove();
}
}
Some important notes when choosing which alarm to use:(for whom who already read the upvoted votes)
The RTC_WAKEUP death valley - time change:
If the user has manually change time to the past the alarm won't go off, and future will cause the alarm to go off immediately if it past the RTC timestamp. Do not use this alarm to do any client side verification / important jobs because it have a chance to fail.
The WAKEUP meaning (Marshmallow and above)
In general - not much. Will not wakeup the device when idle or while in doze, for that alarmManager.setExactAndAllowWhileIdle or alarmManager.setAndAllowWhileIdle (Doze & Idle)