最近项目中遇到手机休眠后Socket心跳发不出去的问题,折腾许久终于解决,所以做个记录。
每种手机都有自己的休眠策略,Android
手机在长时间不操作时会自动让CPU进入睡眠状态,这就导致除AlarmManager外,Android Timer / Handler / Thread.sleep()这些方式实现的定时任务都无法正常运行。
在安卓6.0之后,谷歌又加入了全新的Doze模式,就是说当屏幕关闭一段时间后,系统会对CPU,网络、Alarm等活动做出限制,从而延长电池寿命
但是系统会间歇性退出Doze
模式一小段时间,让应用完成他们的同步操作和Alerm任务。
Android应用处于Doze模式下时会受到以下限制:
1.网络访问被挂起
2.Wake Locks被无视
3.AlarmManager创建的Alarms将被推迟到下一个维护窗口,通过setAndAllowWhileIdle()
以及setExactAndAllowWhileIdle()
设置的Alarms将会在Doze模式下正常执行,同时通过setAlarmClock()
设置的Alarms也会正常执行—Android系统将会在设定时间之前自动退出Doze模式
4.Android系统将会停止执行Wi-Fi的扫描
5.Android系统将会停止Sync Adapter的同步操作
6.Android系统也会停止JobScheduler的定时操作
详细请参考:Android开发技巧(三)—— 创建定时任务https://blog.csdn.net/qq_41205771/article/details/104446141
Doze模式:
Android的Doze模式https://www.jianshu.com/p/d39ac63b445b
所以,要实现手机休眠后Socket正常发送心跳,目前有三种方案:
1.采用AlarmManager进行定时
setAndAllowWhileIdle()
以及setExactAndAllowWhileIdle()
设置的Alarms在Doze模式仍然能够正常执行,并且执行时能够自动唤醒CPU。采用AlarmManager进行定时,到达设定的心跳发送时间后,AlarmManager可以唤醒CPU完成心跳发送的操作。但是经过OPPO HUAWEI等国内厂商实测发现,AlarmManager最短定时时间为5分钟,小于五分钟的定时任务,休眠之后一律会延迟到五分钟在执行。所以此种方式只适合心跳时间大于5分钟的场景,我们项目心跳时间最长为2分钟,所以未采用此方案。
详细测试结果参考:
定时任务AlarmManager的使用注意事项https://blog.csdn.net/sp_wei/article/details/83146655
2.添加白名单
添加Doze白名单后,应用不受Doze的影响,即Doze不会对该应用进行限制,如该应用的Job、Alarm、网络等不会进行限制。不过除了Android的Doze白名单,各厂商基本也都有自己的白名单,俩个都需要添加,否则不起作用。
Doze白名单添加参考:
Doze和App Standby模式下的Android应用适配https://www.jianshu.com/p/f044ce3f5913
各厂商白名单基本在设置->电池->电池优化,需要引导用户手动设置。
查看Doze白名单命令:
adb shell dumpsys deviceidle whitelist
3.WakeLock唤醒锁+Doze白名单方式
唤醒锁使用请参考:
Android中的WakeLock使用https://blog.csdn.net/u012398902/article/details/52838386
持有唤醒锁的应用,锁未释放之前,CPU不会进行休眠。此方式需同时添加Doze白名单和各厂商电池优化白名单,不在Doze白名单的应用,持有的唤醒锁会被无视。不添加各厂商电池优化白名单,实测还是会休眠。
查看Android WakeLock唤醒锁持有列表命令:
adb shell dumpsys "power|grep -i wake"
后记:笔者采用了第三种方案,实测唤醒锁有时会失效,打算同时使用AlarmManager,休眠时定时检测唤醒锁状态,待测试效果。