定时任务 --- Timer, AlarmManager

【记录】记录点滴

【场景】突然想起Android4.4之后,AlarmManager的setRepeating方法变了,而且Android6.0又提出了Doze模式。那么应该怎么样才能保证定时任务的正常运行。

1. AlarmManager

4.4之后setRepeating方法不再精确,虽然可以通过setWindow和setExact方法实现精确的定时任务,但是当休眠后定时任务就不再精确执行了。

附上AlarmManager的setWindow方法的demo,平时运行正常,模拟了电池的Doze模式后,就会出问题。

PS,修改了5s -> 6s,但是log日志时间仍然是间隔5s,并且在另一手机上,却只有1s间隔。

windowLengthMillis The length of the requested delivery window,
*        in milliseconds.  The alarm will be delivered no later than this many
*        milliseconds after {@code windowStartMillis}.  Note that this parameter
*        is a duration, not the timestamp of the end of the window.

看一下windowLengthMillis的注释,英语不是很好,理解上就是windowLenghtMillis设置的不是确切的执行时间。至于不同手机上定时任务差距这么大,应该和系统有关。

windowStartMillis The earliest time, in milliseconds, that the alarm should
*        be delivered, expressed in the appropriate clock's units (depending on the alarm
*        type).

start时间又说与alarm type有关,简单查了下:

闹钟的第一次执行时间,以毫秒为单位,可以自定义时间,不过一般使用当前时间。 需要注意的是,本属性与第一个属性(type)密切相关,如果第一个参数对应的闹钟使用的是相对时间 (ELAPSED_REALTIME和ELAPSED_REALTIME_WAKEUP),那么本属性就得使用相对时间 (相对于系统启动时间来说),比如当前时间就表示为:SystemClock.elapsedRealtime(); 如果第一个参数对应的闹钟使用的是绝对时间(RTC、RTC_WAKEUP、POWER_OFF_WAKEUP), 那么本属性就得使用绝对时间,比如当前时间就表示 为:System.currentTimeMillis()。

 

    private void startAlarm(){
        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
        Intent i = new Intent();
        i.setAction("alarm_action");
        PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
        am.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, 5000, pi);
    }


    BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if("alarm_action".equals(action)){
                Log.e("lxy", "alarm定时任务");
                startAlarm();
            }
        }
    };

 

2. Timer

Timer是基于Thread实现定时任务,模拟Doze模式后,虽然还能正常执行定时任务,但总觉得并没有这么简单:休眠后CPU应该会停止运行,Timer怎么会继续执行。查了别人博客,根据他们的情况,Timer确实不能从根本解决问题。并且在一篇博客中找到了解决方案,这里先做个记录,还没验证。

https://blog.csdn.net/hpc19950723/article/details/70175927/

按照这篇博客的记录,使用唤醒锁可以避免手机进入休眠,也就解决了问题。同时为了验证Timer确实不能满足定时任务的需求,就简单写了个demo,定时在文件中记录,然后手机放在旁边。

3. adb命令模拟进入Doze模式,测试运行结果。

之前的内容中,借鉴了

 http://www.cnblogs.com/cnwutianhao/archive/2017/03/29/6638492.html ,

通过adb模拟了Doze模式。

再记录下:

1)模拟未充电状态

adb shell dumpsys battery unplug

设置后可以通过下面的命令验证

adb shell dumpsys battery

 附上结果的部分截图,看到模拟前,USB powered 是true,表明USB充电。模拟后就变为false了

 定时任务 --- Timer, AlarmManager_第1张图片

再就是IDLE有效化

adb shell dumpsys deviceidle enable

进入模式,这一步,很多博客说明比较简单,按照说明虽然敲了下面的命令,但是AlarmManager仍然正确执行了。

adb shell dumpsys deviceidle step

然后,找到上面提到的博客后,才知道,原来要关闭手机屏幕。之后就可以调用下面的命令行来查看设置的结果

adb shell dumpsys deviceidle

定时任务 --- Timer, AlarmManager_第2张图片

可以看到mState=IDLE,说明正确的进入了模式,这之后AlarmManager也就和预期一样,罢工了。

除了上面这个方法,还可以敲入下面的命令行,强制进入模式

adb shell dumpsys deviceidle force-idle

如果希望恢复手机状态,就连续输入下面两条命令

adb shell dumpsys deviceidle disable
adb shell dumpsys battery reset

//可以输入命令,确认手机是否恢复状态
adb shell dumpsys battery

 

PS:在执行adb shell dumpsys deviceidle step命令,查看设置的结果时,看到了Doze模式的白名单列表。因为可以通过设置应用程序进入白名单列表,来避免Doze模式的限制,所以多看了两眼。

定时任务 --- Timer, AlarmManager_第3张图片

https://zhuanlan.zhihu.com/p/20323263

你可能感兴趣的:(Android)