11. 省电策略之WakeLock限制:部分WakeLock被忽略
各位同学,今天我们来聊一个在Doze模式下非常“硬核”的话题——WakeLock限制。
说白了,就是系统在Doze阶段,会直接无视掉你的一部分WakeLock请求。嗯,你没听错,是“无视”。
我刚开始接触这个机制时,心里也犯嘀咕:WakeLock不是Android里最基础的保活手段吗?怎么还能被忽略?
后来在项目里踩了坑才明白——Doze模式下,系统为了省电,确实会对WakeLock“动刀”。
11.1 哪些WakeLock会被忽略?
先给结论:普通的、非白名单应用的partial WakeLock,在Doze模式下会被系统直接忽略。
为什么会这样?
你想想看,Doze模式的设计初衷就是让设备深度休眠。如果随便一个应用都能用WakeLock把CPU唤醒,那Doze还有什么意义?
我个人习惯把WakeLock分为两类:
- “受信任”的WakeLock:来自系统应用、白名单应用、或者通过高优先级FCM(Firebase Cloud Messaging)触发的WakeLock。
- “普通”的WakeLock:第三方应用直接申请的partial WakeLock,Doze模式下基本失效。
这里有个关键点:full WakeLock和screen dim WakeLock在Doze模式下同样会被限制,因为屏幕都关了,你还想亮屏保活?系统不会答应的。
核心结论:Doze模式下,只有系统级或白名单应用的WakeLock才能正常工作。普通应用的WakeLock请求,系统会直接返回成功,但实际不会生效。
11.2 系统是怎么“忽略”WakeLock的?
我们来看一段简化后的系统源码逻辑(基于Android 12):
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
boolean isDozeWakeLockAllowed(int flags, int ownerUid) {
// 1. 如果是系统应用,直接放行
if (ownerUid == Process.SYSTEM_UID) {
return true;
}
// 2. 检查是否在白名单中
if (isUidInPowerSaveWhitelist(ownerUid)) {
return true;
}
// 3. 检查是否是高优先级FCM唤醒
if (isHighPriorityFcmWakeLock(flags)) {
return true;
}
// 4. 其他情况,在Doze模式下直接拒绝
if (mDeviceIdleMode) {
return false; // 这里就是“忽略”的关键
}
return true;
}
看到第4步了吗?当设备处于Doze模式时,非白名单应用的WakeLock申请,直接返回false。
我在项目中遇到过一个问题:某个第三方SDK在后台频繁申请WakeLock做数据同步。结果Doze模式一开启,WakeLock全部失效,数据同步失败,用户反馈“App怎么不更新了?”
排查了半天,才发现是WakeLock被系统“静默忽略”了——因为申请成功了(acquire返回true),但实际上没起作用。
避坑指南:我曾经以为acquire()返回true就代表WakeLock生效了。其实在Doze模式下,系统会“骗”你——acquire返回成功,但底层根本没给CPU上锁。所以千万别依赖acquire的返回值来判断WakeLock是否真的生效。
11.3 如何绕过这个限制?
嗯,这里要注意,我说的“绕过”不是让你去破解系统,而是用正规途径让WakeLock在Doze模式下也能工作。
主要有三种方式:
| 方式 | 说明 | 适用场景 |
|---|---|---|
| 加入省电白名单 | 引导用户将App加入系统的“不受电池优化”列表 | 需要长时间后台运行的应用(如音乐播放器、运动追踪) |
| 使用高优先级FCM | 通过Firebase Cloud Messaging的“高优先级”消息唤醒设备 | 即时通讯、推送类应用 |
| 申请临时唤醒配额 | 使用JobScheduler或AlarmManager的setAndAllowWhileIdle() | 定时任务、周期性同步 |
我个人最推荐的是第二种方式——高优先级FCM。为什么呢?
因为白名单需要用户手动操作,很多用户根本不知道在哪里设置。而JobScheduler的临时配额又有限制(每个应用每9分钟最多一次)。
高优先级FCM是系统原生支持的唤醒机制,只要你的推送服务器配置正确,Doze模式下也能唤醒设备,然后你可以在onMessageReceived()里申请WakeLock做后续处理。
小技巧:如果你必须使用WakeLock,建议在申请前先检查当前设备是否处于Doze模式。可以用PowerManager.isDeviceIdleMode()来判断。如果处于Doze模式,就不要申请WakeLock了,改用AlarmManager的闹钟来唤醒。
11.4 知识体系总览
下面这张图是我整理的WakeLock在Doze模式下的完整处理流程,你可以对照着理解:
从这张图可以看得很清楚:Doze模式下,只有白名单应用的WakeLock才能正常生效。普通应用即使acquire成功,底层也不会给CPU上锁。
11.5 实战建议
最后,我结合自己的项目经验,给你几条实在的建议:
- 不要依赖WakeLock做核心功能——Doze模式下它可能随时失效。我见过有App用WakeLock做闹钟提醒,结果Doze模式下一觉睡到中午都没响。
- 优先使用JobScheduler——系统会帮你管理唤醒时机,既省电又可靠。
- 如果必须用WakeLock,记得引导用户加入白名单——用Intent跳转到系统设置页,让用户手动添加。
- 测试时一定要开启Doze模式——用
adb shell dumpsys deviceidle force-idle强制进入Doze,看看你的WakeLock是否真的还能工作。
一句话总结:Doze模式下,普通WakeLock就是“纸老虎”——看着能用,实际没用。别被acquire()的返回值骗了。
好了,关于WakeLock限制就讲到这里。下一节我们会聊聊Doze模式下AlarmManager的行为变化,那个坑也不少。
公众号:蓝海资料掘金营,微信deep3321