android解决AlarmManager警报器计时不准,在手机灭屏后延迟的问题

导读:

最近使用AlarmManager的闹钟开发一个提醒业务,在模拟器上测试没有问题,低版本(android6.0以下)的手机上也没有问题,但在android11版本的手机上就出现了“灭屏后延迟的问题”,大概延迟2-5分钟。具体原因是:google为了缓解Android手机饱受诟病的耗电问题,在6.0版本引入了新的省电机制Doze模式,在android6.0之后,如果想继续保持AlarmManager在手机处于所谓Doze模式时仍然能够被即时响应,则需要使用AlarmManager新提供的两个方法setAndAllowWhileIdle()或者setExactAndAllowWhileIdle()。

一、什么是AlarmManager?

AlarmManager(警报器)是Android中的一种系统级别的提醒服务,它会为我们在特定的时刻广播一个指定的Intent。而使用Intent的时候,我们还需要它执行一个动作,如startActivity,startService,startBroadcast,才能使Intent有用。通常我们使用PendingIntent,它可以理解为对Intent的封装,包含了指定的动作。

AlarmManager适合用来开发类似于闹钟的定时任务,通常作为全局的一个定时器来定时启动某段代码。

二、AlarmManager对象中常用的方法有三个:

1、set(int type,long startTime,PendingIntent pi),用于设置一次闹钟。

2、setRepeating(int type,long startTime,long intervalTime,PendingIntent pi),用于设置重复闹钟。每间隔intervalTime毫秒发送一次 PendingIntent

3、setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi),同样是用于设置重复闹钟,但是它是不准确的,相对于setRepeating()方法,更加节能,因为系统会将差不多的闹钟进行合并,以避免不必要地唤醒设备。每间隔大约intervalTime毫秒发送一次 PendingIntent,在间隔时间上有点误差。setRepeating和setInexactRepeating的主要区别在于精确程度,使用setInexactRepeating可以减少电池的消耗,因此优先考虑此方法,对于时间要求比较精准的再考虑使用setRepeating。

  • 参数 int type:AlarmManager的四个唤醒类型,它可以使用以下四个常量:

1、AlarmManager.ELAPSED_REALTIME:使用相对时间,可以通过SystemClock.elapsedRealtime()获取(从开机到现在的毫秒数,包括手机的睡眠时间),设备休眠时并不会唤醒设备。
2、AlarmManager.ELAPSED_REALTIME_WAKEUP:与ELAPSED_REALTIME基本功能一样,只是会在设备休眠时唤醒设备。
3、AlarmManager.RTC:使用绝对时间,可以通过System.currentTimeMillis()获取,设备休眠时并不会唤醒设备。
4、AlarmManager.RTC_WAKEUP:与RTC基本功能一样,只是会在设备休眠时唤醒设备。

相对时间:设备boot后到当前经历的时间,SystemClock.elapsedRealtime()获取到的是相对时间。
绝对时间:1970年1月1日到当前经历的时间,System.currentTimeMillis()和Calendar.getTimeInMillis()获取到的都是绝对时间。

如果是相对时间,那么计算triggerAtMillis就需要使用SystemClock.elapsedRealtime();
如果是绝对时间,那么计算triggerAtMillis就需要使用System.currentTimeMillis()或者calendar.getTimeInMillis()。

  • 参数 long startTime: 闹钟的第一次执行时间,以毫秒为单位
  • 参数 long intervalTime:表示两次闹钟执行的间隔时间,以毫秒为单位。
  • 参数 PendingIntent pi: 绑定了闹钟的执行动作,比如发送一个广播、给出提示等等。

全兼容的AlarmManager示例代码,完美解决android6.0以上灭屏后闹钟延迟问题:

/*得到闹钟管理器*/
AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
/*定义一个闹钟动作(taskService是一个自定义的服务)*/
Intent intent = new Intent(context, taskService.class);
PendingIntent pi = PendingIntent.getService(context, 0, intent, 0);
//版本大于Android 6.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pi);
}
//版本大于Android 4.4
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    //单次闹钟:
    //alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pi);
    //重复闹钟:
    alarmManager.setWindow(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 5 * 1000,pi);
} else {
    /*重复闹钟*/
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 5 * 1000, pi);
}

 

已标记关键词 清除标记