Android 6.0中Alarm Manager在Doze模式下的问题

11

我制作了一个应用,在Android 6.0之前一直能够正常工作。我认为是Doze功能不允许我的闹钟触发。

我使用sharedpreferences来处理选项:

//ENABLE NIGHT MODE TIMER
    int sHour = blockerTimerPreferences.getInt("sHour", 00);
    int sMinute = blockerTimerPreferences.getInt("sMinute", 00);

    Calendar sTime = Calendar.getInstance();
    sTime.set(Calendar.HOUR_OF_DAY, sHour);
    sTime.set(Calendar.MINUTE, sMinute);

    Intent enableTimer = new Intent(context, CallReceiver.class);
    enableTimer.putExtra("activate", true);
    PendingIntent startingTimer = PendingIntent.getBroadcast(context, 11002233, enableTimer, PendingIntent.FLAG_UPDATE_CURRENT);
    AlarmManager sAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    sAlarm.setRepeating(AlarmManager.RTC_WAKEUP,
            sTime.getTimeInMillis(),
            AlarmManager.INTERVAL_DAY, startingTimer);

这里出了什么问题,有任何线索吗?

这是一个用于屏蔽来电的应用程序。谢谢!

编辑:我有3个文件(还有更多...)如下:

MainActivity (All code)
CallReceiver (Broadcast that triggers the alarm again (reboot etc))
CallReceiverService (Handles the call / phone state)

1
使用 adb shell dumpsys alarm 确认您的闹钟已被预约。请注意,setRepeating() 在 Android 4.4+ 上是不准确的。 "这也会导致电池耗尽" -- 这似乎不太可能。不运行的代码应该不会消耗电池。 - CommonsWare
嗨@CommonsWare,52个唤醒和52个闹钟...它总是在响闹钟。我认为setRepeating()会在那一分钟触发,没有问题。 - FilipeOS
@FilipeOS,你搞定了吗?我们能在Doze模式下实现alarmManager.setRepeating()吗? - devgeek
@devgeek 只喜欢这样,并创建一个服务来检查启动等。在 >M 上设置 setExactAndAllowWhileIdle,而在旧版本上设置 setExact。 - FilipeOS
setExact不会重复执行。如果我需要让它重复执行怎么办?@FilipeOS - devgeek
2个回答

14

Doze模式会延迟您的闹钟响起时间直到下一个维护窗口。为了避免Doze模式 阻止您的闹钟,您可以使用 setAndAllowWhileIdle(), setExactAndAllowWhileIdle()setAlarmClock()。您将有大约10秒的时间来执行您的代码,设置下一个闹钟(对于带有 _AndAllowWhileIdle 的方法,每15分钟最多只能执行一次)。

如果您想测试Doze模式,可以使用ADB命令:
  1. 使用Android 6.0(API级别23)或更高版本的系统镜像配置硬件设备或虚拟设备。

  2. 将设备连接到开发机并安装您的应用程序。

  3. 运行您的应用程序并保持其活动状态。
  4. 关闭设备屏幕。(应用程序仍然处于活动状态。) 强制系统通过运行以下命令循环进行Doze模式:

    adb shell dumpsys battery unplug

    adb shell dumpsys deviceidle step

  5. 您可能需要多次运行第二个命令。重复操作,直到设备状态变为空闲。

  6. 观察设备重新激活后您的应用程序的行为。确保应用程序在设备退出Doze时能够优雅地恢复。

编辑:增加setAlarmClock示例

不要忘记检查SDK级别(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)

AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent intent = new Intent(this, MyAlarmReceiver.class); //or just new Intent() for implicit intent 
//set action to know this come from the alarm clock
intent.setAction("from.alarm.clock");
PendingIntent pi = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//Alarm fire in 5s.
am.setAlarmClock(new AlarmManager.AlarmClockInfo(System.currentTimeMillis() + 5000, pi), pi);

1
嗨@xiaomi,只需将setRepeating替换为setAlarmClock?使用相同的参数?你能否举个例子?谢谢! - FilipeOS
这只是一个示例,5秒后触发警报。请选择您想要的时间。 - xiaomi
啊,我没注意到setAlarmClock会在状态栏中显示时钟图标。顺便说一下,set操作只适用于你检查接收器的intent.getAction()时使用。要移除图标,你必须使用setAlarmClock()之外的另一种方法。 - xiaomi
只需使用setAndAllowWhileIdle即可。 - xiaomi
@xiaomi 如果我们需要在Doze模式下实现alarmManager.setRepeating(),该怎么做呢?我们可以像您上面提到的那样使用setAlarmClock吗? - devgeek
显示剩余3条评论

2
如果设备处于Doze模式,您需要使用以下API之一:setExactAndAllowWhileIdlesetAndAllowWhileIdle
请注意,在Doze模式下没有用于唤醒设备的重复闹钟API,因此,如果您需要在Doze模式下使用重复闹钟来唤醒设备,则必须使用上述API,并在定时器触发时重新设置计时器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接