24. Doze模式与Android 12的睡眠模式(Sleep Mode):新变化
各位好,今天我们聊一个有意思的话题——Android 12引入的睡眠模式。
说实话,我第一次看到这个新特性时,心里想的是:「这不就是Doze模式的马甲吗?」但深入研究后,我发现事情没那么简单。Android 12的睡眠模式,其实是对Doze的一次重要补充和重构。
为什么要有睡眠模式?
先说说背景。Doze模式从Android 6.0开始引入,到Android 11已经相当成熟。但我在项目中遇到过一个典型问题:用户晚上睡觉时,手机明明放在那里不动,Doze却没能完全阻止后台唤醒。
为什么会这样?因为Doze的触发条件是「设备静止且屏幕关闭一段时间」。但有些App会利用小技巧——比如每隔几分钟发一个低优先级通知,或者用JobScheduler卡着Doze的维护窗口做点小动作。这些行为虽然不致命,但确实影响了待机功耗。
Android 12的睡眠模式,说白了就是针对「用户真正在睡觉」这个场景做的深度优化。
睡眠模式 vs Doze模式:核心区别
我整理了一张对比表,方便大家理解:
| 特性 | Doze模式(Android 6~11) | 睡眠模式(Android 12+) |
|---|---|---|
| 触发条件 | 设备静止 + 屏幕关闭 | 用户睡眠时间(基于行为预测) |
| 网络访问 | 维护窗口期可访问 | 完全禁止(除非白名单) |
| 闹钟唤醒 | 允许setExactAndAllowWhileIdle | 仅允许setAlarmClock |
| JobScheduler | 延迟执行 | 完全挂起 |
| 系统行为 | 分阶段限制 | 一次性进入深度限制 |
嗯,这里要注意:睡眠模式不是取代Doze,而是叠加在Doze之上的更严格策略。
睡眠模式的触发机制
Android 12通过DeviceStateManager和SleepModeController来管理睡眠模式。触发条件不再是简单的「屏幕关闭+静止」,而是引入了用户行为预测。
我记得在调试某款原型机时,发现睡眠模式总是无法触发。后来查了源码才发现,系统需要收集至少7天的用户行为数据,才能建立睡眠模型。这包括:
- 每天最后一次解锁时间
- 每天第一次解锁时间
- 夜间屏幕关闭后的活动模式
核心代码路径大致如下:
// frameworks/base/services/core/java/com/android/server/deviceidle/DeviceIdleController.java
private boolean shouldEnterSleepMode() {
// 1. 检查是否处于预测的睡眠时间段
if (!mSleepModePredictor.isSleepTime()) {
return false;
}
// 2. 检查设备是否静止
if (!isDeviceIdle()) {
return false;
}
// 3. 检查屏幕是否关闭超过阈值
if (mScreenOffTime < SLEEP_MODE_SCREEN_OFF_THRESHOLD_MS) {
return false;
}
// 4. 检查是否有用户活动
if (hasRecentUserActivity()) {
return false;
}
return true;
}
说白了,系统会判断「你现在是不是真的在睡觉」,而不是「手机是不是没动」。
睡眠模式下的应用限制
进入睡眠模式后,应用的待遇比Doze更「惨」。我给大家列几个关键点:
核心变化:网络访问被完全切断
在Doze模式下,App还能在维护窗口期访问网络。但在睡眠模式下,除非App在白名单中,否则网络访问被完全禁止。这意味着后台同步、心跳包、甚至某些系统服务都会被限制。
我曾经帮一个团队排查过问题:他们的即时通讯App在Android 12上收不到消息。查了半天,发现是睡眠模式把网络切断了,而他们的FCM通道没走白名单。解决方案很简单——把App加到DEVICE_IDLE_USER_WHITELIST中。
白名单机制的变化
Android 12的白名单机制也做了调整。以前Doze的白名单是全局的,现在睡眠模式有自己独立的睡眠白名单:
// 获取睡眠模式白名单
List<String> sleepWhitelist = deviceIdleController.getSleepModeWhitelist();
// 检查App是否在睡眠白名单中
boolean isWhitelisted = sleepWhitelist.contains(packageName);
我建议各位开发者注意:不要滥用白名单。Google在CTS测试中会检查睡眠白名单的App数量,超过阈值会直接fail。我在项目中就遇到过这种情况——某个预装App为了做数据上报,申请了睡眠白名单,结果CTS测试没过。
对开发者的影响
说实话,Android 12的睡眠模式对大多数App影响不大。但如果你做的是以下类型的应用,就需要特别注意:
- 即时通讯类:必须走FCM高优先级通道,或者申请白名单
- 健康监测类:使用
setAlarmClock而不是setExact - 后台同步类:考虑使用
WorkManager的EXPEDITED任务
避坑指南
我曾经在适配Android 12时,发现某个App的定时任务在睡眠模式下完全不执行。后来查了文档才知道,setExactAndAllowWhileIdle在睡眠模式下会被降级为setWindow。正确的做法是使用setAlarmClock,这个API在睡眠模式下仍然保证准时触发。
睡眠模式的状态机
为了让大家更直观地理解,我画了一张状态转换图:
从图中可以看到,睡眠模式是一个独立的状态,它可以从Doze或深度Doze进入。一旦进入睡眠模式,限制会更严格,直到用户主动唤醒设备。
调试与测试建议
最后,我分享几个调试睡眠模式的实用命令:
# 强制进入睡眠模式
adb shell dumpsys deviceidle force-sleep
# 退出睡眠模式
adb shell dumpsys deviceidle exit-sleep
# 查看睡眠模式状态
adb shell dumpsys deviceidle | grep -A 10 "SleepMode"
# 查看睡眠白名单
adb shell dumpsys deviceidle whitelist | grep "sleep"
注意事项
在测试睡眠模式时,记得先让系统收集足够的用户行为数据。否则force-sleep命令可能不会生效。我一般会在测试机上先跑一周的模拟数据,或者直接修改SleepModePredictor的阈值参数。
好了,关于Android 12睡眠模式的新变化,今天就聊到这里。这个特性虽然看起来只是Doze的补充,但实际上对后台行为的限制力度大了不少。各位在适配Android 12时,一定要重点测试睡眠模式下的应用行为,尤其是那些依赖后台定时任务和网络通信的App。
公众号:蓝海资料掘金营,微信deep3321