最近我们公司的产品被客户投诉定时关机偶现不准时和偶现不起作用的问题。具体复现操作是定时一段比较长的时间进行自动关机就很容易出现该概率问题。

        由于这个定时关机功能是前同事实现的,所以我得查看代码,查看跟踪代码发现该定时方式使用的是使用了AlarmManager里的set()方法:

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, setTime, pendingIntent);

        我自己调试了发现短时间的定时是可以正常实现功能的,然后调了一个十个小时的定时关机发现确实有误差,延迟了三分钟才关机。 

查阅文档等资料:

android 定时器使用 android alarmmanager定时失效_时间戳

        官方文档提示API19(android4.4)开始, 为了节能省电(减少系统唤醒和电池使用)。使用Alarm.set()和Alarm.setRepeating()已经不保证精确性。API19版本以上应该使用另外两个精确的Alarm方法,setWindow()和setExact()。

但是在Android8.0中使用了setExact()方法后还是发现长时间定时会不准时,继续查阅资料发现官方在API23(android6.0)后添加了新的应用接口:

android 定时器使用 android alarmmanager定时失效_定时关机_02

setExactAndAllowWhileIdle()。

        但是奇葩的事情还是出现了,在Android8.0中使用该接口后设置十个小时长时间定时,然后息屏放置,发现还是会出现不准时的问题,延迟了足足两分钟。

        此时已经没有耐心了,果断决定换一种实现方式,不使用AlarmManager去定时了,稳定性实在令人堪忧。

解决方法

最后使用轮询去比较设定的关机时间和当前时间。

        在这里我采取注册监听 ACTION_TIME_TICK这个系统广播,系统会在每隔一分钟发送一个ACTION_TIME_TICK广播:

android 定时器使用 android alarmmanager定时失效_时间戳_03

 

        每接收到一个广播就进行一次比较,将设定的关机时间戳和当前时间进行比较,如果比较的时间的绝对值小于60,就进行相应的操作。

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {

            final String action = intent.getAction();
           
            if (action.equals(ACTION_TIME_TICK)) {

             //setMillionSeconds为设定的定时时间戳
            if (Math.abs(setMillionSeconds - System.currentTimeMillis()) <= 60000) {

                //to do what you whant to do 

            }             
          }
        }
    };

        使用这种方式比使用AlarmManager稳定多了!AlarmManager设定的时间能不能回调回来就是一个玄学!

不过需要注意的是,ACTION_TIME_TICK广播需要动态注册