6.0及以上无法准确设置日程时间

Unable to set schedule time accurately on 6.0 and above

我正在尝试制作一个需要准确安排时间的应用程序。我需要在特定时间重置应用程序内生成的日志(驾驶员职责信息)。

我尝试使用 setAlarm 但它在 6.0 及更高版本上无法正常工作。因为它应该在没有互联网的情况下工作,所以我不能使用 Push Notification. 我也尝试使用 setAlarmClock 但它也不准确。

我需要alarm准确1小时和24小时

更新

记录警报

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;
}

传感器报警

 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 + "");
}

群组报警

 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);

    }

}

我总共使用了 4 个闹钟,其中 1 个需要每 5 分钟安排一次,休息时间为 1 小时、8 小时和 24 小时。那么传感器警报(每 5 分钟一次)是否导致其他警报出现问题?

请提出最佳解决方案

注意:SO 上的所有其他问题均无效。

显然这个问题是由于每 5 个警报都会引起问题 min.So 其他警报会导致失败,因为两个警报同时触发。

所以我的建议是将您的其他闹钟时间设置为 1 或 2 分钟later/before(即 8 小时 2 分钟而不是 8 小时)。

注意:1 或 2 分钟应该完全取决于您的铃声持续时间。

有几种方法可以解决这个问题。

首先注意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.

您可以阅读有关权衡的更多信息here。话虽这么说,我会在保持所有警报的基础上回答。同一文档中的另一引述提到:

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.

这实际上指的是Doze Mode,其中指出:

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

盗用 Google 的图像 documentation on dozing:

这对你来说意味着,如果你有彼此接近的警报,设备将在下一个维护周期中对它们进行批处理,但如果你设置 targetSdkVersion 更低,您可以轻松修复排除批处理行为(但以其他编译改进为代价)。

假设您确实坚持打瞌睡行为,切换到扩展 WakefulBroadcastReceiver 而不是常规 BroadCastReceiver 可以帮助确保您有时间响应 RTC_WAKEUP 警报。

您还可以使用多个 BroadcastReceiver,以便 Android 分别处理它们(而不是将它们分批为每分钟一个/维护)。这只是意味着不同的未决意图在不同的接收者上得到处理:

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

从你的代码中我不清楚 eLogNotice 接收器是否是其他警报正在使用的同一接收器的实例。