I have an alarm clock app on the play store that works very well on most of the devices , but unfortunately some devices report the alarm does not fire on the time adjusted and i concluded from research that there are some devices that restrict apps running in the background and kills alarm manager !
I have handled doze mode using the following code :
if (Build.VERSION.SDK_INT >= 23)
{
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeStamp, pendingIntent);
}
else if (Build.VERSION.SDK_INT >= 19)
{
alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeStamp, pendingIntent);
}
else
{
alarmManager.set(AlarmManager.RTC_WAKEUP, timeStamp, pendingIntent);
}
However this seems not enough on some devices.
I have read that a foreground service can prevent the system from killing the app anyway , however i can't ensure this also since i don't have in hand the devices where the problem occurs.
I want to ensure my alarm works perfectly fine on all devices and handles all scenarios , so what are all possible things to do to make sure my app runs properly and is not killed by the system?
You should be using AlarmManagerCompat.setAlarmClock() to set a user visible alarm suitable for an alarm clock app. This API uses setAlarmClock() on API 21+, which as per its documentation:
these alarms will be allowed to trigger even if the system is in a low-power idle (a.k.a. doze) mode. The system may also do some prep-work when it sees that such an alarm coming up, to reduce the amount of background work that could happen if this causes the device to fully wake up -- this is to avoid situations such as a large number of devices having an alarm set at the same time in the morning, all waking up at that time and suddenly swamping the network with pending background work. As such, these types of alarms can be extremely expensive on battery use and should only be used for their intended purpose.
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 want to create repeating service by alarm manager. if app is running in live time then it is correct running every one min. it in sleep mode to running every five min. why it is not running every one min in sleep mode?
public static final long NOTIFY_INTERVAL = 60000;
AlarmManager alarms = getAlarmMAnager();
alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), NOTIFY_INTERVAL, recurringAlarm);
That is very much expected and can happen due to various reasons
Android Oreo has limitations on running services in background, so you may face this on O devices
Doze mode on Android Marshmallow onwards can cause this, it will stop all network operations itself & take away CPU wake locks
AlarmManager is not meant to for doing repeated operations in background
I will suggest you to use JobsSchedulers or Firebase Dispatchers for tasks which you want to execute in background, as it will take care of Doze mode, background service limitations, no network scenarios etc.
I am using AlarmManager in our app to set alarms that are set at specific date and time. Now recently few users of our app are complaining that these alarms are not popping up. Also, in Android O guidelines, it is mentioned that app should not run any background service and instead should switch to Firebase JobDispatcher. I have following 2 questions
In our app, we do not do any background task except to show notification to user at the specified time and date. Even in this case, should we switch to Firebase Jobdispatcher?
In case we do need to switch to JobDispatcher, how can the Job be set to run at exactly specific date and time?
Because you're not doing any background tasks like network requests or long running CPU operations, I think you should continue using Alarm Manager.
Now recently few users of our app are complaining that these alarms are not popping up.
This is because when doze mode triggers, it is not guaranteed that your alarms will be triggered(see this).
What you can do is use setAndAllowWhileIdle() or setExactAndAllowWhileIdle() methods from AlarmManager class as per your requirement for API level >= 23.
According to the documentation these are appropriate for notification purposes.
setAndAllowWhileIdle() documentation:
Like set(int, long, PendingIntent), but this alarm will be allowed to
execute even when the system is in low-power idle modes. This type of
alarm must only be used for situations where it is actually required
that the alarm go off while in idle -- a reasonable example would be
for a calendar notification that should make a sound so the user is
aware of it. When the alarm is dispatched, the app will also be added
to the system's temporary whitelist for approximately 10 seconds to
allow that application to acquire further wake locks in which to
complete its work.
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.
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.