闲置时,Android AlarmManager不会在第二天触发警报

时间:2018-02-06 08:26:27

标签: android alarmmanager alarm android-broadcastreceiver

我知道在这个主题上有很多关于SO的类似线程,但我找不到真正解决问题/或确定根本原因的线程。

首先,我正在瞄准SDK 22(Android 5.1),这意味着我可以使用AlarmManager + WakefulBroadcastReceiver + IntentService,即使这不是最新的做事方式。 我对JobScheduler等解决方案不感兴趣,我只是想了解发生了什么以及为什么。

我正在测试的手机有Android 8.0,但是因为我的目标是Android 5.1,所以无关紧要。

所以我正在处理的代码会为第二天06:00设置闹钟。

private fun setupAlarm() {
    val calendar = Calendar.getInstance()
    calendar.timeInMillis = System.currentTimeMillis()
    calendar.add(Calendar.DAY_OF_YEAR, 1)
    calendar.set(Calendar.HOUR_OF_DAY, 6)
    calendar.set(Calendar.MINUTE, 0)

    val alarmIntent = Intent(this, AlarmReceiver::class.java)
    val alarmPendingIntent = PendingIntent.getBroadcast(this, 1221, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT)

    val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, AlarmManager.INTERVAL_DAY, alarmPendingIntent)
}

AlarmReciever仅启动服务:

class AlarmReceiver : WakefulBroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        startWakefulService(context, Intent(context, DownloadingIntentService::class.java));
    }
}

此服务然后尝试下载文件,完成后调用completeWakefulIntent(intent)方法,让系统知道它完成了它的工作。

我无法弄清楚它什么时候起作用,什么时候不起作用。一天早上,它做了它应该拥有的东西,另一方面它没有。

我设置了一个远程LogCat功能,以查看IntentService是否已启动但到目前为止我看不到它的任何日志,因此这意味着不会触发警报。

如果我在下一分钟设置闹钟,甚至重复一个它应该的工作。但是,当我把明天早上的时间缩短时,那是非常不可靠的。

感谢您的帮助。

4 个答案:

答案 0 :(得分:1)

我自己也遇到了这个问题。看看setRepeating方法会发生什么,让android系统调整警报被触发的时间。它很可能会尝试批量不同的警报,以优化电池使用。但在常规情况下,如果手机没有打瞌睡......它通常会在正确的时间发出警报。

但是,如果手机闲置一段时间,手机会进入打盹模式,因此报警会延迟。我个人观察到最多1个半小时的延误。

如果您希望它完全触发,您必须使用setExactAndAllowWhileIdle方法或setAlarmClock方法。在这种情况下,您必须自己处理下一个警报的日程安排。这些方法适用于打盹模式,并在准确的时间点击警报。

这些方法也有缺点。 setExactAndAllowWhileIdle方法只能用于每九分钟左右调度一次最大警报。 setAlarmClock方法主要向用户显示类似常规警报的通知,并将指示警报的详细信息(此行为因os版本的不同而异)

答案 1 :(得分:0)

我使用此代码每天触发备份。它对我有用,试一试。

AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context, MyReceiver.class);
    intent.setAction("CUSTOM_INTENT");
    alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, 06);
    calendar.set(Calendar.MINUTE, 00);

    // setRepeating() lets you specify a precise custom interval--in this case,
    // 1 day
    alarmMgr.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis()/1000,
            AlarmManager.INTERVAL_DAY, alarmIntent);

答案 2 :(得分:0)

试试这个:

    Calendar now = Calendar.getInstance();
        Calendar alarm = Calendar.getInstance();
        alarm.set(Calendar.HOUR_OF_DAY, hourOfDay);
        alarm.set(Calendar.MINUTE, minute);
        long alarmMillis = alarm.getTimeInMillis();
        if (alarm.before(now)) alarmMillis+= 86400000L;  //It will add 1 day if your time selected before now
        //set alarm method of yours\\

settingAlarmManager(requestCode,alarmMillis);

private void settingAlarmManager(String requestCode, Calendar calendar) {
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        Intent notificationIntent = new Intent(AddTaskActivity.this, AlarmReceiver.class);
        PendingIntent broadcast = PendingIntent.getBroadcast(AddTaskActivity.this,
                Integer.valueOf(requestCode), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, broadcast);
  }

我的接收者课程:

public class AlarmReceiver extends BroadcastReceiver {


    @Override
    public void onReceive(Context context, Intent intent) {
        //do your stuff here\\
    }
}

清单:

<receiver android:name=".utils.AlarmReceiver" />

我已尝试上面的代码来设置闹钟并执行我的自定义任务。也许这会对你有帮助。

答案 3 :(得分:0)

一些事情:

  • 计划Alarm的时间越早,the less precise it will be
  • 虽然您定位的是API级别22,但较高Android版本中已弃用的元素可能无法完全发挥作用,WakefulBroadcastReceiver
  • 的情况就是如此
  • 您正在尝试在Android 8.0中运行background job。值得探索Foreground Service

    • 可以从后台开始
    • 在手机闲置时,正确通知用户您确实正在做某事。
    • 不要担心可能需要几秒钟才能完成的任务。
  • 您可能已经杀死了您的申请。当用户手动杀死某个应用时,在大多数设备中,所有AlarmPendingIntent也会被杀死。

许多开发人员使用的调度策略不是设置重复Alarm,而是有两个单独的Alarms连续重新安排(@Kushan在他的回答中提到了类似的内容)。

简而言之:

在一天中尽快启动Scheduler(即使用户打开您的应用,也可以多次触发)。检查是否已存在所需的PendingIntents(您的后台作业)。如果他们不这样做,只需安排他们。同样,在11.55左右安排另一个Alarm

所有这些午夜调度程序必须在5分钟内重新安排主AlarmManager,然后安排作业和午夜警报< / em>接下来几天。

此方法允许您:

  • 安排完全警报,因为重复的警报没有确切的选项。
  • 减少预定警报的时间距离,操作系统通常会更精确地处理这些警报。
  • 避免因过去安排而立即触发的警报

此外,尝试从您正在使用的API版本中获取最多

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        this.alarmManager.setExactAndAllowWhileIdle(
                AlarmManager.RTC_WAKEUP,
                rtcStartingTime.getTimeInMillis(),
                pendingIntent
        );
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        this.alarmManager.setExact(
                AlarmManager.RTC_WAKEUP,
                rtcStartingTime.getTimeInMillis(),
                pendingIntent
        );
} else {
        this.alarmManager.set(
                AlarmManager.RTC_WAKEUP,
                rtcStartingTime.getTimeInMillis(),
                pendingIntent
        );
}