Background
I'm currently developing an application for Android which revolves around an alarm that goes of on an user specified time. My intent for it is that it will be used for waking people up in the morning (and the following morning - aka repeating alarm). When the alarm goes of it will call an Activity that has a couple of options (such as snooze). I've got all of this working but I'm running in to a problem.
Problem
I'm using AlarmManager to handle my alarm needs. There is however something curious going on with the class (in my opinion). There are two adviced ways to handle the setting of the alarm time. Namely setInexactRepeating and setRepeating. The problem with these functions are:
setInexactRepeating is not very accurate. My tests have shown that this gets approximately activated at the specified time, which the documentation indicates, all be it rather vaguely;
the alarm will not fire before this time, but there may be a delay of almost an entire alarm interval before the first invocation of the alarm.
My tests show there is usually something of a 5 minute delay. On this answer the user has an average delay of approximately 12 minutes. This won't do, of course, for a system that is supposed to wake people up at their specified times.
setRepeating does trigger at the specified time. The docs specify however that as of API 19 that all repeating alarms are inexact. Which is exactly what I don't want.
As of API 19, all repeating alarms are inexact. Because this method has been available since API 3, your application can safely call it and be assured that it will get similar behavior on both current and older versions of Android.
There is a setExact method, but this is a bit too specific. Aside from that it does not give me the option to have a certain interval (for repeating the alarm daily). Edit: After trying to go with setExact I found that this would require me to move up to API 19 (currently on 15), which is something I would like to avoid.
Question
Am I using the wrong class for this system? To me it seems like it should be a legit usage, but reading through the docs has left me wondering. Is there perhaps another class which is better suited for this system?
You can separate before API 19 and after API 19. While setting alarm for the first time:
if (Build.VERSION.SDK_INT >= 19) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), mondayIntent);
} else {
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
AlarmManager.INTERVAL_DAY * 7, mondayIntent);
}
When you catch the alarm:
if (Build.VERSION.SDK_INT >= 19) {
rescheduleAlarm();
}
You must set the alarm with
setexact
again in rescheduleAlarm.
Hope this helps.
Related
I have a widget that displays the current time and some other info, therefore it has to update once every minute. I am using an AlarmManager that triggers every minute and calls an IntentService that updates the widget. This solution works fine in Android versions prior to Oreo. On Oreo, this solution does not work - it looks like the AlarmManager is not firing. I have read that there is a TextClock control now that automatically updates the text to the current time but unfortunately this is not a solution since I need to draw other non-text information on the widget.
The alarm is set as follows:
if (android.os.Build.VERSION.SDK_INT >= 19) {
alarmManager.setExact(AlarmManager.RTC, timeInMillis(), intent);
} else {
alarmManager.setRepeating(AlarmManager.RTC, timeInMillis(), 1000 * 60, intent);
}
Note that the alarm is rescheduled every time it fires on android 19+ since it is not a repeating alarm.
Is there an alternative solution for the above to work in Android O and later?
If you are using simple TextViews to display the Clock, use a TextClock instead of TextView (added in API Level 17)
If you are using more complex Views (e.g. you draw a bitmap), you have to use a foreground service, which either uses explicit TIME_TICK receivers or Handlers to schedule the updates.
You have to show a foreground notification and if not properly implemented, it could drain a lot of battery...
I am implementing a widget that checks on-line train departure times between every minute and every hour, depending on the time of day.
Calling the service with
manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() - 10000, 60000, pendingIntent)
works fine, but for debugging I would like to reduce the interval to about 10 seconds which cannot be done because of the 1-minute limit in more recent versions of Android. Clearly, I don't care about battery life in the emulator.
As far as I understand, using an Hander/Timer is not an option, because it required the task to be in the foreground. Is a visible widget "in the forground"?
What is the recommended practice in this case?
you actually have to tasks
configure the alarmmanager to add/remove trigger events via intents
interprete the events with intents in a service
If you seperate both you can easily create a very simple gui/activity that does the same as the alarmmanager would do when being triggerd and that you can debug:
* onSendButtonClick: create and send pendingIntent
for the alarmmanager-handling i would implement logging into a text file each time alarmmanager is added/removed/triggered.
Be prepared that newer android versions may postpone alarmmanager events to save energy until the device is already active and that intervals less than 15 minutes may not work.
you may also need on_boot_complete to reconfigure alarmmanager after device-shutdown
AlarmManager on API19 has the method setExact() to set an exact alarm.
Exact means --> If I set an alarm to 2:01 pm it will be triggered at 2:01 pm
On API 23 - Marhsmwallow (6.0) there is a new method setExactAndAllowWhileIdle(), but as of the reference it is not EXACT because it will trigger only every minute and in low power idle mode only every 15 minutes.
Exact != every 15 minutes :-)
So how can I achieve an exact alarm with AlarmManager in 6.0?
If a user adds a reminder or a calendar appointment and wants to be informed 10 minutes before the event it should show the alarm EXACT 10 minutes before the event. With setExactAndAllowWhileIdle() this seems is not possible.
Reference Link:
http://developer.android.com/reference/android/app/AlarmManager.html#setExactAndAllowWhileIdle(int, long, android.app.PendingIntent)
So how can I achieve an exact alarm with AlarmManager in 6.0?
You are welcome to try setAlarmClock(), as AFAIK it is unaffected by Doze mode. Otherwise, AlarmManager is not a viable option for you. Even having your app on the battery optimization whitelist will not help, as AlarmManager behavior does not change based on the whitelist.
You are welcome to use GCM, as a high-priority message should give you an opportunity to alert the user. This, of course, requires network connectivity.
The only offline solution that I am aware of — and that I am presently testing — is to have the user add your app to the battery optimization whitelist, then use a foreground service (to try to keep your process around), a ScheduledExecutorService (for the timing), and a partial WakeLock (to keep the CPU on). This will be fairly devastating to the user's battery.
Using setExactAndAllowWhileIdle() for a one-time alarm will fire exactly on the given time even in Doze idle mode. So this probably is the way to go.
Problems start, if you want to repeat the alarm at a rate of < 15 min (or set any other at a time < 15 min away from the last one), as this will not work in Doze idle mode, where such alarms are forced to the next 15 min or are executed when idle maintenance starts, which happens for about ten minutes first after 1 hour, then after another 2 hours, then after another 4 hours and so on.
- EDIT -
As of today Nov 17, Dianne Hackborn writes in this Post's comments:
"For what it's worth, the minimum time between while idle alarms will be changing to 9 minutes at some point relatively soon (even on devices running the current Marshmallow builds)."
This doesn't change anything fundamentally though.
Here are my discussion with Ian Lake on Google+!
setExactAndAllowWhileIdle() is exact and should work.
The 15 minutes time frame is wrong in the java doc.
I was trying to create an automation system running in the background. My frequency range was between 1-15 minutes. My wish was not to use a foreground service. By looking at the name of the method "setExactAndAllowWhileIdle", I thought that yeah it is safe to go with one-time alarms, scheduling the next one when done.
However, I couldn't find a way to run code in doze mode with alarms running more frequent than 15 minutes. Instead, I choose to start a foreground service when doze mode gets activated and stop that foreground service when phone awakes. User won't be seeing your foreground notification while using his/her phone. I don't care much about the ones in doze mode.
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if(intent.getAction().equals("android.os.action.DEVICE_IDLE_MODE_CHANGED")){
if (pm.isDeviceIdleMode()) {
//startAutomationForegroundService();
} else {
//stopAutomationForegroundService();
return;
}
AutomationReceiver.completeWakefulIntent(intent);
return;
}
}
You need to register "android.os.action.DEVICE_IDLE_MODE_CHANGED" intent filter into your WakefulBroadcastReceiver. Care putting it into manifest may not help.
I was ready through the documentation and i was having some questions about setReating and setInexactRepeating. I was reading some other posts, but i could't find an answere.
The documentation says for both:
Note: as of API 19, all repeating alarms are inexact.
Does is mean that both methods are exactly the same in api 19 and above? Also how inexact is inexact? And if there is any delay, what are the effects for the following alarm?
Thanking in advance.
As one can read at the end of the official documentation as of API 19 [and future versions] all calls to setRepeating() will delegate to setInexactRepeating() instead. So as of KitKat and upcoming versions both methods do the exact same thing.
The delay will not effect the following alarms refering to the official documentation.
Schedule a repeating alarm that has inexact trigger time requirements;
for example, an alarm that repeats every hour, but not necessarily at
the top of every hour. These alarms are more power-efficient than the
strict recurrences traditionally supplied by setRepeating(int, long,
long, PendingIntent), since the system can adjust alarms' delivery
times to cause them to fire simultaneously, avoiding waking the device
from sleep more than necessary.
Your alarm's first trigger will not be before the requested time, but
it might not occur for almost a full interval after that time. In
addition, while the overall period of the repeating alarm will be as
requested, the time between any two successive firings of the alarm
may vary. If your application demands very low jitter, use one-shot
alarms with an appropriate window instead; see setWindow(int, long,
long, PendingIntent) and setExact(int, long, PendingIntent).
As of API 19, all repeating alarms are inexact. Because this method
has been available since API 3, your application can safely call it
and be assured that it will get similar behavior on both current and
older versions of Android.
What are the parameters of the following:
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
AlarmManager.INTERVAL_FIFTEEN_MINUTES, alarmIntent);
And of the following:
alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, alarmIntent);
What is the difference and also how are the two different in terms of functionality?
Both examples schedule a repeating alarm that will send the given alarmIntent. On both cases, the first time it is sent will be immediate (calendar.getTimeInMillis() returns the current time). On both cases, the device will be woken up when the alarm needs to be sent (as evident by AlarmManager.RTC_WAKEUP).
There are two differences between these calls. The simpler one is that the intent will be sent every fifteen minutes on the first call, and every day on the second call (as you can see in the third parameter). The more complicated difference is the function call itself: setRepeating will schedule the first alarm for exactly every fifteen minutes; setInexactRepeating will schedule the second alarm for approximately every 24 hours, meaning it might deviate from that interval - with the advantage of consuming less power.
Do notice that this has changed in API 19, where these two calls are synonymous. See this guide, and this API documentation.
Decide how precise your alarm needs to be
Choosing the alarm type is often the first step in creating an alarm. A further distinction is how precise you need your alarm to be.
For most apps, setInexactRepeating() is the right choice. When you use this method, Android synchronizes multiple inexact repeating alarms and fires them at the same time. This reduces the drain on the battery.
For the rare app that has rigid time requirements as example, the alarm needs to fire precisely at 4:00 p.m. everyday then use setRepeating().
Reference: Decide how precise your alarm needs to be
To augment previous answers, there are a number of other best practices to consider when using repeating alarms, particularly inexact alarms requested using setInexactRepeating().
Alarm Type
Non-WAKEUP alarms are better than WAKEUP alarms from a power management perspective. Using the former your alarm may fire late, but it will still fire either when the device is woken by the user, or when another wakeup alarm fires. Using WAKEUP alarms will wake the device out of sleep, consuming additional battery and potentially causing other inexact alarms to fire that have been delayed that could otherwise have been delayed longer (reducing the batching power-saving benefits that inexact alarms provide).
Prefer alarms using the ELAPSED timebase rather than the RTC timebase. The former are more likely to have a more random distribution across devices than RTC alarms, which reduces the risk of network congestion and on the server if the alarm is triggering some sort of poll. Phones running Gingerbread (or older) suffer from a bug whereby RTC inexact alarms have a tendency to align closely to the real-time clock, e.g. approximately 30s past each quarter of an hour. ELAPSED alarms don't suffer from this bug on these earlier platform versions. Even if your alarm doesn't trigger any network activity, remember that if it is a wakeup alarm it may trigger other alarm non-wakeup intents that may hit the network.
Timebase
Be careful to specify the requested start time in the correct time domain for the alarm type. Failure to do this can result in alarms being set in the past (they fire right away) if setting an RTC alarm with an ELAPSED timebase or far in the future if setting an ELAPSED alarm using the RTC timebase. You can check what alarms an app has scheduled using dumpsys alarm via the adb shell.
Interval
Specifying an inexact alarm interval of anything other than the interval constants defined in the AlarmManager API is redundant on SDK <19: they will be scheduled as exact not inexact alarms, losing all the power-saving benefits that inexact alarms provide.
Edit: here's further explanation of the bug relating to gingerbread and honeycomb 3.0 devices: https://code.google.com/p/android/issues/detail?id=31550
setRepeating is more accurate and setInexactRepeating is for saving battery but no accurate , setInexactRepeating is good for maintenance in background for example and setRepeating is necessary for example for alarm clock .
Use setInexactRepeating() is used when app is not seriously used to required for example wake up early in the morning . if Alarm wake up approximately that time there is no any life dangeous.
like medicine pills app where highly critical patient use that app to reminde nurse or doctor staff the is compulsory to use setRepeating().
When you use setInexactRepeating()
then Android synchronizes repeating alarms from multiple apps and fires them at the same time(once). This reduces the total number of times the system must wake the device.
thus reducing drain on the battery.
repeating alarms are inexact. Note that while setInexactRepeating() is an improvement over setRepeating() also
it can still overwhelm a server if every instance of an app hits the server around the same time. Therefore, for network requests, add some randomness to your alarms.