6

I have soooo much trouble getting my radio alarm clock working as intended and I have read a lot of threads here about that topic, but unfortunatley none did help me.

AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);        
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent penInt = PendingIntent.getBroadcast(this, intentId, intent, 0);

This method of distinction between the API levels I found here on stackoverflow and put it inside my calcNextAlarm() function (plus some log messages for debugging) to set the alarm correctly no matter what API is being used on the device:

// problems in doze mode api 23+
if (Build.VERSION.SDK_INT >= 23) {
    if (testMode) Log.d("Ben", "setAlarmClock() - API 23+");
    am.setAlarmClock(new AlarmManager.AlarmClockInfo(alarmTimeInMillis, penInt), penInt);
}

else if (Build.VERSION.SDK_INT >= 19) {
// Wakes up the device in Idle Mode
    if (testMode) Log.d("Ben", "setExact() - API >= 19 && API < 23");
    am.setExact(AlarmManager.RTC_WAKEUP, alarmTimeInMillis, penInt);
}
// Old APIs
else {
    if (testMode) Log.d("Ben", "set() - API < 19");
    am.set(AlarmManager.RTC_WAKEUP, alarmTimeInMillis, penInt);
}

According to the Log.d messages I can see that, on my Android 7.1 Device the first method setAlarmClock() is being executed to set the alarm in the Receiver.

I am getting really desperate after 3 weeks of unsuccessful tests and coding - my alarm today got off 4 minutes too late again - this should never happen, according to the doze mode training page:

Alarms set with setAlarmClock() continue to fire normally — the system exits Doze shortly before those alarms fire.

On my 7.1 phone the alarm will even be 20 seconds to 1:40 mins late when I set the alarm to "now +5 or 6" minutes. Could anyone advise me how to really ALWAYS get the alarm off perfectly in time?

Ben
  • 265
  • 3
  • 13

2 Answers2

6

Try using:

setExactAndAllowWhileIdle()

This will ensure that the alarm fires on time. I have tested it in my own app and it's reliable.

If you are targeting API less than 23, keep this inside an if clause which checks the current api installed in the device. Use this only above API level 23, for rest keep using setExact

All right to answer your comment:

1) i have accurately tested it in doze mode without the battery being plugged in

2) yea unfortunately there's the one alarm per 9 minutes limit for while idle type alarms. You have two alternatives here:

First use the setExactAndAllowWhileIdle to fire the first alarm.

a) For snooze, you will have to use setAlarmClock method

b) for snooze, you can have a JobScheduler job scheduled with minimum latency time set as your snooze time. This will ensure that the job gets at least scheduled at the gap of the snooze interval. But just this will cause the job to get randomly scheduled after the interval so you'll also need to have an override deadline set to 0 so that the job gets scheduled immediately after the minimum latency. Also keep network requirements as none and idle mode needed as default or false. This is also as reliable as the alarm exact methods as per my experience and i personally use this approach.

Kushan
  • 5,855
  • 3
  • 31
  • 45
  • This will also not show a potential notification to the user like setAlarmClock method. Now it's not absolutely exact to the t but it's relatively accurate to around 5 - 10 seconds off according to my experience. Last desperate alternative would be to have a high priority FCM message hitting the device at the time you want but this will depend on network connectivity. – Kushan Dec 28 '17 at 14:27
  • 1
    I am sure I tried that before, but I'm so desperate, I will give it a shot :) 1: Are you sure it's accurate on batteray and not being charged? (Because all the methods seem to be perfectly accurate when on power cable and not in doze mode) 2: According to the doze training page: `Note: Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can fire alarms more than once per 9 minutes, per app.` If I want to "snooze" for 5 minutes - should I use the normal **setAlarmClock()** then for the snooze instead of **setExactAndAllowWhileIdle()**? 3: Target API 27 and min SDK is 16. – Ben Dec 28 '17 at 14:53
  • Yea try it, the two i specified and the one you're already using are the only ones to deal with doze mode anyway. Allow while idle works best for me. Tested it on a few devices. Always works fine for me – Kushan Dec 28 '17 at 14:55
  • Sorry I didn't realize the comment would be sent when pressing **enter** :) I edited it a bit, in case you didn't notice: could you review my questions please? – Ben Dec 28 '17 at 15:28
  • Check my answer, there's two alternatives now for your predicament. With minimum sdk 16, I'm not sure about job scheduler, so i guess you can use the a) option. Otherwise the job scheduler is the best for latest devices as android tends to screw people of they don't use it nowadays – Kushan Dec 28 '17 at 15:40
  • Okay I implemented the **...allowWhileIdle** now for the "main" alarms and the **setAlarmClock** for the 5minute snooze timers. The rest I left as is inside the IF. I'll have to test it a few days now i guess, thank you so far - I will upvote/accept your answer as soon I can confirm it's working good. – Ben Dec 28 '17 at 15:43
  • Np man :) hope it works. Android framework sucks sometimes :( – Kushan Dec 28 '17 at 15:44
  • I just saw you edited your answer - thanks for the explanation! As of now I'm going with **setAlarmClock()** for the snooze thingy, but in a first test it got off after 6 instead of after 5 minutes - which is "borderline okay" but not nice... maybe I'll try your other solution some time soon. (Other than that, I still wanna test the **setExact...** thing for a few days before I mark your answer accepted) – Ben Dec 28 '17 at 20:17
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/162125/discussion-between-ben-and-kushan). – Ben Dec 28 '17 at 20:21
  • Cool, np man :) take your time.. alarms are never fully accurate anymore but the options we are already using in the answer and your post are the only ones available. I think you'll just have to give up and have faith that it will at least fire – Kushan Dec 28 '17 at 20:22
  • 1
    Seems to be working well as of now. (An additional problem I think was my Motorola E4 Plus, which is VERY buggy on stock 7.1.1 and sometimes the clock just "hangs" - I believe that was also accumulating into the late alarm problem... greets) – Ben Jan 02 '18 at 02:51
  • 1
    Can anybody confirm that setAlarmClock is less exact than setExactAndAllowWhileIdle() ? – Yuriy Kulikov Jul 24 '19 at 19:26
  • 1
    only in doze mode, in evey other place i am guessing both are equally accurate – Kushan Jul 24 '19 at 21:08
  • @Kushan What if I am setting two alarms say 8:45 and 8:47 , so what should be the correct approach here like should I use setExactAndAllowWhileIdle for first one and setAlarmClock for the second ??? – syed_noorullah Oct 18 '19 at 15:46
  • There actually might be someting wrong with Motorola devices - on my Motorola Moto One with Android 10 even clock alarms set by the stock alarm clock app are sometimes delayed (usually up to 5 minutes). I tested all available ways of setting alarm and the best results give me the setAlarmClock method (see @badadin answer). – Derek K Feb 01 '21 at 10:37
2

The documentation states that:

  • If you need to set alarms that fire while in Doze, use setAndAllowWhileIdle() or setExactAndAllowWhileIdle().

  • Alarms set with setAlarmClock() continue to fire normally — the system exits Doze shortly before those alarms fire.

In "Adapt your app to Doze" section it also says that:

Note: Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can fire alarms more than once per 9 minutes, per app.

The documentation for setExactAndAllowWhileIdle says that:

Unlike other alarms, the system is free to reschedule this type of alarm to happen out of order with any other alarms, even those from the same app. This will clearly happen when the device is idle (since this alarm can go off while idle, when any other alarms from the app will be held until later), but may also happen even when not idle. Note that the OS will allow itself more flexibility for scheduling these alarms than regular exact alarms, since the application has opted into this behavior. When the device is idle it may take even more liberties with scheduling in order to optimize for battery life.

So setExactAndAllowWhileIdle is not guaranteeing the exact execution, and setAlarmClock is more exact (according to documentation).

badadin
  • 507
  • 1
  • 5
  • 18