-2

I'm trying to make an app where it requires schedule time to be accurate. I need to reset the log(driver duty info) generated within app at a specific time.

I tried to use setAlarm but its not working accurately on 6.0 and above. As it should work without internet I can't go with Push Notification.I also tried to use setAlarmClock but it doesn't work accurate either.

I need alarm to be accurate for 1 hour and 24 hour

Update

Log Alarm

public static void startLogAlarm(Context ctx,long milliseconds) {

    final int SDK_INT = Build.VERSION.SDK_INT;

    // 1 Day = 86400 seconds = 86400000 milliseconds
    long timeInMillis = System.currentTimeMillis() + milliseconds;

    AlarmManager alarmManager = (AlarmManager) ctx
            .getSystemService(Context.ALARM_SERVICE);

    Intent intent = new Intent(ctx, eLogReceiver.class);
    intent.setAction(Utility.eLOG_ACTION);
    PendingIntent pi = PendingIntent.getBroadcast(ctx, 3, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    if (SDK_INT < Build.VERSION_CODES.KITKAT) {

       alarmManager.set(AlarmManager.RTC_WAKEUP, timeInMillis, pi);

    } else if (Build.VERSION_CODES.KITKAT <= SDK_INT && SDK_INT < Build.VERSION_CODES.M) {

       alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pi);

    } else if (SDK_INT >= Build.VERSION_CODES.M) {

    //  AlarmManager.AlarmClockInfo alarmClockInfo = new  AlarmManager.AlarmClockInfo(timeInMillis, pi);
    //  alarmManager.setAlarmClock(alarmClockInfo, pi);

        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, pi);

    }
}

// Start Alarm
Utility.startLogAlarm(this, Constants.DAY_MILLISECONDS);

public class Constants {

   public static final long HOUR_MILLISECONDS = 3600000;
   public static final long DAY_MILLISECONDS = 86400000;
}

Sensor Alarm

 public static void startSensorAlarm(Context ctx, long timeInMillis) {

    final int SDK_INT = Build.VERSION.SDK_INT;

    AlarmManager alarmManager = (AlarmManager) ctx
            .getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(ctx, AlarmReceiver.class);
    intent.setAction(Utility.SENSOR_ACTION);
    PendingIntent pi = PendingIntent.getBroadcast(ctx, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    if (SDK_INT < Build.VERSION_CODES.KITKAT) {
        alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeInMillis, pi);
    } else if (Build.VERSION_CODES.KITKAT <= SDK_INT && SDK_INT < Build.VERSION_CODES.M) {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeInMillis, pi);
    } else if (SDK_INT >= Build.VERSION_CODES.M) {

        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeInMillis, pi);

     //AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(System.currentTimeMillis()+timeInMillis, pi);
        alarmManager.setAlarmClock(alarmClockInfo, pi);
    }

    showLog("Sensor Alarm Time", timeInMillis + "");
}

Group Alarm

 public static void startGroupAlarm(Context ctx) {

    final int SDK_INT = Build.VERSION.SDK_INT;

    AlarmManager alarmManager = (AlarmManager) ctx
            .getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(ctx, AlarmReceiver.class);
    intent.setAction(Utility.GROUP_UPDATE_ACTION);
    PendingIntent pi = PendingIntent.getBroadcast(ctx, 2, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    if (SDK_INT < Build.VERSION_CODES.KITKAT) {
        alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + INTERVAL_EVERY_8_HOURS, pi);
    } else if (Build.VERSION_CODES.KITKAT <= SDK_INT && SDK_INT < Build.VERSION_CODES.M) {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + INTERVAL_EVERY_8_HOURS, pi);
    } else if (SDK_INT >= Build.VERSION_CODES.M) {
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + INTERVAL_EVERY_8_HOURS, pi);
    }

    Utility.showLog("Group Alarm", "Started");

}

LogNoticeAlarm

 public static void startLogNoticeAlarm(Context ctx, long milliseconds) {

    final int SDK_INT = Build.VERSION.SDK_INT;

    // 1 Hour = 3600 seconds = 3600000 milliseconds
    long HourMilliseconds = System.currentTimeMillis() + milliseconds;

    AlarmManager alarmManager = (AlarmManager) ctx
            .getSystemService(Context.ALARM_SERVICE);

    Intent intent = new Intent(ctx, eLogNoticeReceiver.class);
    intent.setAction(Utility.eLOG_NOTICE_ACTION);
    PendingIntent pi = PendingIntent.getBroadcast(ctx, 4, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    if (SDK_INT < Build.VERSION_CODES.KITKAT) {
        alarmManager.set(AlarmManager.RTC_WAKEUP, HourMilliseconds, pi);
    } else if (Build.VERSION_CODES.KITKAT <= SDK_INT && SDK_INT < Build.VERSION_CODES.M) {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, HourMilliseconds, pi);
    } else if (SDK_INT >= Build.VERSION_CODES.M) {
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + milliseconds, pi);

     // AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(HourMilliseconds, pi); // alarmManager.setAlarmClock(alarmClockInfo, pi);

    }

}

I've been using total 4 alarms out of them 1 required to be schedule at every 5 minutes and rest are 1 hours, 8 hours and 24 hours. so is that sensor alarm( every 5 minutes) is causing the problem for other Alarm?

Please suggest the best solution

Note : All other questions on SO are not working.

moDev
  • 5,248
  • 4
  • 33
  • 63
  • What behavior are you experiencing with `setExactAndAllowWhileIdle()`? As long as your alarms are not within 15 minutes of each other, that should do an exact alarm to the second even through Doze mode. – Nick Friskel Mar 17 '17 at 20:10
  • how do you calculate initial milliseconds as well – Nick Cardoso Mar 17 '17 at 22:55
  • How inaccurate are we talking? Have you tried with a different requestcode than 3, in case it's conflicting? – Nick Cardoso Mar 17 '17 at 23:01
  • @NickCardoso No there is no conflict with request code. I've hardcoded milliseconds for an hour and a day – moDev Mar 18 '17 at 03:51
  • What happens in 6.0 and above? – Pravin Divraniya Mar 18 '17 at 13:56
  • I hope you already went through with 'setExactAndAllowWhileIdle' documentation. – Pravin Divraniya Mar 18 '17 at 14:12
  • 1
    You would not require help if your code was all working perfectly. Please show how you "hardcoded milliseconds for an hour and a day" and how you are sure "there is no conflict with request code". If you want a working answer don't be dismissive of things that help in problem determination – Nick Cardoso Mar 19 '17 at 17:58
  • @NickCardoso I've updated code – moDev Mar 20 '17 at 09:03
  • Hey, you still haven't stated what is actually going wrong? Which alarm is not accurate, by how much? What is/isnt happening differently to what you expect? – Nick Cardoso Mar 21 '17 at 12:01
  • I set all the alarm during login and keep the device aside for some hours. I don't see hourly/daily alarm executed. If device is awake for an hour it works with hourly and other alarm(5 minutes) remains `inexact` but I'm more concerned with daily and hourly alarm – moDev Mar 21 '17 at 12:22
  • All my alarms are in-exact but I want few of them(daily and hourly) to be exact to make my app work. @NickCardoso – moDev Mar 22 '17 at 05:43
  • Do you see a better solution to my problem? @NickCardoso – moDev Mar 23 '17 at 11:36
  • Did you see my comment on Neo's answer? – Nick Cardoso Mar 23 '17 at 11:40
  • Yeah I saw but didnt got how it can fix? Can we make all the alarms exact including 5 minutes? If no then what are the possible solutions we can play with to make it more accurate. – moDev Mar 23 '17 at 11:44
  • What If i remove code for 5 min alarm and test? Do you think it can fix it? – moDev Mar 23 '17 at 11:45
  • I'll add an answer now – Nick Cardoso Mar 23 '17 at 11:49

2 Answers2

1

There are several way you could address this problem.

Firstly pay attention to this (paraphrased) quote from the Android Alarm Documentation:

Alarm Manager is intended to run your code at a specific time, even if your app is not currently running. For normal timing operations (ticks, etc) it is easier and much more efficient to use a Handler.

You can read more about the tradeoffs here. That being said I'll answer on the basis of keeping all as alarms. Another quote from the same documentation mentions:

Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups ... Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior.

This actually refers to Doze Mode, which states:

AlarmManager alarms (including setExact()) are deferred to the next maintenance window.

To steal Google's image from the same documentation on dozing:

doze vizualization

What this means to you is, if you have alarms close to eachother, the device will batch them in the next maintenance cycle but if you set targetSdkVersion lower, you have an easy fix to excluding the batching behaviour (but at the cost of other compiled-in improvements).

Presuming you actually stick with the Doze behaviour, switching to extending WakefulBroadcastReceiver insted of regular BroadCastReceiver can help ensure you have time to respond to the RTC_WAKEUP Alarms.

You can also use more than one BroadcastReceiver so that Android handles them separately (rather than batching them to one per minute / maintenance). This would just mean the different pending intents get handled on different receivers:

Intent intent = new Intent(ctx, AlarmReceiver.class);
...
Intent intent = new Intent(ctx, AlarmReceiver2.class); //a different receiver class
...

I'm not clear from your code whether the eLogNotice receiver is an instance of the same receiver the other alarms are using.

Nick Cardoso
  • 20,807
  • 14
  • 73
  • 124
  • eLogNotice receiver has different receiver. In face 1 hour and 24 hours alarms have different receiver. – moDev Mar 23 '17 at 12:47
  • I'd say the first change to try is using Wakeful receives then as I've linked to. Small change to make – Nick Cardoso Mar 23 '17 at 12:49
  • App is already live so we can't go for lower target version. Yes, I'll try with wakeful receiver shortly – moDev Mar 23 '17 at 12:52
  • It started to work but still its not accurate. Hourly Alarm remain accurate for 6 hours thereafter it slightly got delayed by 7 minutes, now it remains 7 minute delay every hour. – moDev Mar 25 '17 at 06:10
  • Alarms are getting executed but they are not accurate. can we make it accurate specially with 24 hr alarm? – moDev Mar 27 '17 at 10:49
  • I've marked your answer just because alarms are getting executed but it doesnt work accurate – moDev Mar 28 '17 at 13:23
0

Obviously this one is causing problem due to alarm in every 5 min.So other alarm leads to failure because two alarm hits at the same time.

So my suggestion is set your other alarm time 1 or 2 min later/before(i.e. 8 hrs 2 min instead of 8 hrs ).

Note: 1 or 2 min should exactly decides by your ringtone duration.