11

Using Android 19+

setExact in conjuction with WakefulBroadcastReceiver sometimes does not fire on time (can be a few seconds or so late). I mean most it of the time it does. probably 49 times out of 50 its correct.

I'm not sure if its just because the system is busy at the time and it can't handle the workload or what

Here is how I set the alarm:

  AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
  Intent intent = new Intent(AlarmReceiver.INTENT_FILTER);
  PendingIntent alarmIntent = PendingIntent.getBroadcast(context, MyApplication.ALARM_REQUEST_CODE, intent,  PendingIntent.FLAG_UPDATE_CURRENT);
  alarmMgr.setExact(AlarmManager.RTC_WAKEUP, timeToWakeUp, alarmIntent);

Here is my receiver code:

public class AlarmReceiver extends WakefulBroadcastReceiver {

public static final String INTENT_FILTER = "myfilter";

@Override
public void onReceive(Context context, Intent intent) {
    Intent service = new Intent(context, MyWakefulService.class);
    startWakefulService(context, service);

}

}

And in the WakefulService

public class MyWakefulService extends IntentService {

....

@Override
protected void onHandleIntent(Intent intent) {

....
MobileMon
  • 8,341
  • 5
  • 56
  • 75
  • When you're calculating the time for timeToWakeUp, are you resetting the seconds and milliseconds to 0? – JDJ Jul 13 '14 at 16:38
  • @JDJ I set the seconds accordingly, however I don't set the milliseconds. I'll try that – MobileMon Jul 13 '14 at 16:41
  • @JDJ well I believe setting the milliseconds makes the timer even more precise so thanks for that. However it doesn't solve the issue at hand. I'm pretty sure its just because the system is so busy that the event couldn't fire at that exact time. Because it always happens if I reboot the device and I schedule the alarm prior to boot being finished. Of course that's not the only time there is a delay but I think I'll chalk it up to system being busy unless I hear otherwise – MobileMon Jul 13 '14 at 17:26

2 Answers2

24

For Marshmallow era(?), we need some ugly codes like below... :( And "delayInMillis" param should be more than 15 minutes on the API 23. If not, system ignore the minutes less than 15 minutes.

private void registerExactAlarm(PendingIntent sender, long delayInMillis) {
    final int SDK_INT = Build.VERSION.SDK_INT;
    AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
    long timeInMillis = (System.currentTimeMillis() + delayInMillis) / 1000 * 1000;     //> example

    if (SDK_INT < Build.VERSION_CODES.KITKAT) {
        am.set(AlarmManager.RTC_WAKEUP, timeInMillis, sender);
    }
    else if (Build.VERSION_CODES.KITKAT <= SDK_INT  && SDK_INT < Build.VERSION_CODES.M) {
        am.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, sender);
    }
    else if (SDK_INT >= Build.VERSION_CODES.M) {
        am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, sender);
    }
}
cmcromance
  • 2,289
  • 1
  • 25
  • 26
  • Hello, where did you get from that the triggerAtMillis(timeInMillis) should be higher than 15 minutes? I can not find any documentation regarding this. thank you – Loebre Jul 14 '16 at 14:55
  • And if I may another question, why don't you use the setExact(...) for Build.VERSION > M? Thanks – Loebre Jul 14 '16 at 14:57
  • @Loebre "setExact" method doesn't set timers exactly anymore on Mashmellow. If you want to be called back on 07:00 AM and use "setEaxct" method. System doesn't call you on 07:00 AM when in the DOZE mode. I think you should read some documents about DOZE. – cmcromance Jul 22 '16 at 15:57
  • setExactAndAllowWhileIdle is not repeat the alarm in marshmallow. I want to start the service some interval time – Saravanan Nov 29 '16 at 03:24
  • @Saravanan Sorry for the late reply. You're right. So, We should call that codes repeatedly after receiving callback. Call registerExactAlarm() and receive callback from the system and call registerExactAlarm again. Let me know if there are an another solution. – cmcromance Jun 04 '17 at 14:05
  • @Loebre, the 15 seconds thing is in the docs, read carefully.. Thank's for the code snippet Savaranan :) – HedeH Feb 20 '18 at 14:48
8

This behaviour is added in API 19:

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.

from AlarmManager.

Important: setExact() still does not have to be exact, as the docs state:

The alarm will be delivered as nearly as possible to the requested trigger time.

Manuel Allenspach
  • 12,467
  • 14
  • 54
  • 76
  • This is wrong. set() is now in inexact. However, they added setExact() for when you need to be exact (API 19+) – MobileMon Jul 16 '14 at 11:37
  • 1
    Yes, that's why added the second paragraph. It still can be inexact, it is just delivered **as nearly as possible**. – Manuel Allenspach Jul 16 '14 at 11:53
  • Everything you said helps, but still every now and then it will fail to fire (10 seconds late or so). is it just because the system is busy??? I don't see any other reason – MobileMon Jul 25 '14 at 11:30
  • Here is how I set the on Android 19+ setWindow(AlarmManager.RTC_WAKEUP, timeForNextAlarm, 0, alarmIntent) – MobileMon Jul 25 '14 at 11:41