17、功耗管理:Suspend/Resume、Wakelock、Doze 模式、功耗调试工具
功耗管理,说白了就是让设备在不干活的时候尽量少耗电,在需要干活的时候能快速醒过来。我做了这么多年嵌入式Android,见过太多因为功耗问题导致产品翻车的案例。有一次客户反馈设备待机一晚上掉电30%,查到最后发现是一个外设的驱动没做suspend/resume,整晚都在轮询。嗯,这种坑踩过一次就再也不会忘了。
17.1 Suspend/Resume 机制:系统的睡眠与唤醒
Suspend/Resume 是Linux内核最基础的电源管理机制。Android设备按下电源键灭屏后,系统并不会完全关机,而是进入某种低功耗状态。
内核支持的几种睡眠状态:
| 状态 | 描述 | 功耗 | 唤醒延迟 |
|---|---|---|---|
| Suspend-to-Idle | 仅CPU空闲,外设保持供电 | 中等 | 极短 |
| Standby (Power-on Suspend) | CPU暂停,内存自刷新 | 较低 | 短 |
| Suspend-to-RAM (STR) | CPU掉电,内存保持 | 很低 | 中等 |
| Suspend-to-Disk (Hibernate) | 系统镜像写入磁盘,完全断电 | 零 | 长 |
Android设备通常使用 Suspend-to-RAM。流程是这样的:
- 用户空间通过
/sys/power/state写入 "mem" - 内核冻结用户态进程和内核线程
- 逐个调用设备的 suspend 回调函数
- 关闭非唤醒源的中断
- CPU 进入深度睡眠
唤醒时反向操作:唤醒源(比如电源键、RTC闹钟)触发中断,CPU恢复执行,设备 resume 回调被调用,系统恢复运行。
关键点:每个设备驱动都必须正确实现 suspend/resume 回调。我曾经遇到过触摸屏驱动在suspend时没有关闭中断,导致设备刚睡下就被触摸中断唤醒,一晚上反复睡眠唤醒几百次,电池直接崩了。
17.2 Wakelock:谁在阻止系统睡眠?
Wakelock 是Android特有的机制,用来阻止系统进入suspend状态。说白了就是一个"别睡,我还有事"的锁。
Wakelock 分为两种类型:
- Partial Wakelock:阻止CPU进入suspend,但屏幕可以灭。常用于后台音乐播放、下载任务。
- Full Wakelock:阻止CPU和屏幕都进入低功耗。现在基本被废弃了,因为太耗电。
在应用层获取 Wakelock 的代码示例:
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"MyApp:BackgroundTask"
);
wl.acquire(10 * 60 * 1000L); // 最多持有10分钟
// 执行后台任务...
wl.release();
注意:Wakelock 必须成对使用 acquire/release。忘记 release 会导致系统无法睡眠,这是最常见的功耗问题之一。我建议在开发阶段开启 adb shell dumpsys power 来监控 Wakelock 持有情况。
内核侧也有 Wakelock 机制,通过 /sys/power/wake_lock 和 /sys/power/wake_unlock 控制。驱动开发者可以用 wake_lock_init() 创建内核 Wakelock。
17.3 Doze 模式:Android 6.0 引入的省电大招
Doze 模式是Google在Android 6.0(API 23)引入的。它的核心思想是:设备静止且灭屏一段时间后,系统进入深度省电状态。
Doze 模式的工作阶段:
- 轻量Doze:灭屏后30分钟进入。禁止网络访问,延迟JobScheduler任务。
- 深度Doze:灭屏后更长时间(取决于设备)。进一步限制应用活动。
- 维护窗口:系统定期(约9分钟)退出Doze,允许应用执行紧急任务。
应用如何适配 Doze?
- 使用
setAndAllowWhileIdle()设置闹钟,可以在Doze模式下触发 - 使用 Firebase Cloud Messaging (FCM) 高优先级消息唤醒设备
- 避免在后台频繁使用 Wakelock
避坑指南:我曾经开发一个即时通讯应用,在Doze模式下收不到消息。原因是用了普通的AlarmManager,被Doze延迟了。后来改用 setExactAndAllowWhileIdle() 才解决。但要注意,这种闹钟每小时只能触发几次,频繁使用会被系统忽略。
17.4 功耗调试工具:找到耗电元凶
调试功耗问题,光靠猜是不行的。我常用的工具有这些:
17.4.1 dumpsys power
最直接的命令,查看系统电源状态:
adb shell dumpsys power
输出中重点关注:
Wake Locks:当前持有的Wakelock列表Display Power:屏幕状态mWakefulness:Asleep/Awake/DreammDozeScreenState:Doze模式状态
17.4.2 Battery Historian
Google官方工具,可视化分析耗电数据:
adb bugreport bugreport.zip
# 上传到 https://bathistorian.appspot.com/ 或本地部署
它能展示CPU唤醒次数、Wakelock持有时间、网络活动、传感器使用等。我个人习惯在每次功耗测试后都导出bugreport,对比不同版本的耗电差异。
17.4.3 /proc/wakelocks 和 /sys/power
内核级别的调试接口:
adb shell cat /proc/wakelocks
adb shell cat /sys/power/wakeup_count
adb shell cat /sys/power/state
查看哪些内核驱动持有了Wakelock,以及系统支持的睡眠状态。
17.4.4 电流计与功率分析仪
硬件级别的调试工具。我常用的是 Monsoon 功率分析仪,可以精确测量设备在不同状态下的电流变化。配合 systrace 可以关联代码执行和电流波形。
实战经验:有一次设备待机电流偏高,用 Battery Historian 发现一个名为 "SensorService" 的Wakelock 持续持有。进一步排查发现是某个第三方应用注册了加速度传感器的监听,但没在灭屏时取消注册。解决方案是在 onPause() 中取消传感器监听。
17.5 知识体系总览
下面这张图总结了本章的核心知识点和它们之间的关系:
从图中可以看出,系统电源状态是顶层决策者,它决定了设备是清醒还是睡眠。Suspend/Resume 是内核层面的睡眠机制,Wakelock 是阻止睡眠的锁机制,Doze 是应用层面的省电策略。而调试工具贯穿始终,帮助我们定位问题。
我的建议:刚接触功耗调试的同学,先从 dumpsys power 和 Battery Historian 入手。这两个工具能覆盖80%的功耗问题。等遇到疑难杂症时,再上功率分析仪和内核trace。
功耗管理是个系统工程,从硬件设计到驱动开发,从框架层到应用层,每个环节都可能成为耗电的漏洞。但只要你掌握了 Suspend/Resume、Wakelock、Doze 这三个核心机制,再加上趁手的调试工具,大部分问题都能迎刃而解。
公众号:蓝海资料掘金营,微信deep3321