16、Logcat 与功耗调试:WakeLock、AlarmManager 日志解读

功耗调试,说白了就是跟手机“偷电”的那些模块斗智斗勇。我做了这么多年系统优化,最头疼的就是用户反馈“手机待机一晚上掉电20%”。这种问题,八成跟 WakeLock 和 AlarmManager 脱不了干系。

今天我们就来聊聊,怎么通过 Logcat 日志,揪出那些“不睡觉”的 App。

16.1 WakeLock 日志:谁在阻止系统休眠?

WakeLock 是 Android 系统里一个很“霸道”的机制。它允许 App 阻止 CPU 休眠,或者阻止屏幕变暗。你想想看,如果某个 App 一直抱着 WakeLock 不放,系统就永远没法进入深度睡眠,耗电自然就上去了。

16.1.1 如何抓到 WakeLock 日志?

我个人习惯,先开启 WakeLock 的日志开关。在 adb shell 里执行:

# 开启 WakeLock 详细日志
adb shell dumpsys power | grep "Wake Locks"

但这样只能看到当前状态。要实时跟踪,得用 logcat 过滤:

adb logcat -s PowerManagerService:D

嗯,这里要注意,不同厂商的 ROM 可能 tag 不一样。有的叫 PowerManagerService,有的叫 PowerMS。我在项目中遇到过小米的机器,tag 居然是 PowerKeeper,当时排查了好久。

16.1.2 日志解读:一条典型的 WakeLock 记录

看这条日志:

D/PowerManagerService( 1234): acquireWakeLock: flags=0x1, tag="MyApp:MyLock", uid=10123, pid=5678

拆开来看:

  • flags=0x1:表示这是一个 PARTIAL_WAKE_LOCK,只阻止 CPU 休眠,不阻止屏幕。
  • tag="MyApp:MyLock":App 自己起的名字。我建议开发者在这里写清楚用途,比如 "MyA:DownloadLock"。
  • uid=10123:App 的用户 ID,可以通过 ps 命令反查包名。

如果看到这条日志频繁出现,而且没有对应的 releaseWakeLock 日志,那基本可以断定这个 App 在“偷电”。

⚠️ 注意: 有些 App 会故意用空字符串作为 tag,或者用随机字符串,让你查不到来源。遇到这种情况,我一般直接看 uid,然后用 adb shell dumpsys package 反查包名。

16.1.3 避坑指南:超时 WakeLock

我曾经遇到一个案例,某个视频 App 播放完视频后,WakeLock 没有释放。但奇怪的是,日志里明明有 releaseWakeLock 的记录。

后来发现,问题出在 acquireWakeLock 时设置了超时时间:

D/PowerManagerService( 1234): acquireWakeLock: flags=0x1, tag="VideoPlayer", timeout=30000

这个 timeout=30000 表示 30 秒后自动释放。但 App 在 30 秒内又调用了 acquire,导致超时时间不断重置。说白了,就是 App 开发者偷懒,用超时机制代替了手动释放。

正确的做法是什么?应该在 onPause()onStop() 里显式释放 WakeLock,而不是依赖超时。

16.2 AlarmManager 日志:谁在频繁唤醒系统?

AlarmManager 是另一个耗电大户。它允许 App 在指定时间唤醒系统执行任务。如果某个 App 设置了大量闹钟,或者闹钟间隔很短,系统就会被频繁唤醒。

16.2.1 查看所有已注册的闹钟

用这个命令:

adb shell dumpsys alarm

输出内容很多,我一般重点关注这几个字段:

字段 含义 排查要点
package 包名 看是不是第三方 App
type 闹钟类型 ELAPSED_REALTIME_WAKEUP 会唤醒系统
when 触发时间 看是不是频繁触发
interval 重复间隔 小于 1 分钟的就要警惕

16.2.2 Logcat 中的 AlarmManager 日志

过滤命令:

adb logcat -s AlarmManager:D

典型的日志长这样:

D/AlarmManager( 1234): Sending alarm: Alarm{type=2, when=1234567890, interval=60000, tag=*walarm*:com.example.app/.MyReceiver}

解读一下:

  • type=2:表示 ELAPSED_REALTIME_WAKEUP,会唤醒系统。
  • interval=60000:每 60 秒触发一次。说实话,这个频率有点高。
  • tag=*walarm*:com.example.app/.MyReceiver*walarm* 前缀表示这是一个唤醒闹钟。

如果你看到某个 App 的 *walarm* 日志每分钟出现一次,那基本可以断定它在“刷存在感”。

💡 小技巧:adb shell dumpsys alarm | grep "com.example.app" 可以只看某个 App 的闹钟信息。我经常用这个来快速定位问题 App。

16.3 知识体系:功耗调试核心逻辑

下面这张图,是我自己总结的功耗调试流程。你跟着走一遍,基本能解决 80% 的问题。

功耗调试核心逻辑 ① 发现问题 用户反馈耗电快 / 待机异常 ② 抓取日志 logcat / dumpsys ③ WakeLock 分析 检查 acquire / release 是否成对 ③ AlarmManager 分析 检查唤醒频率和间隔 ④ 定位问题 App

16.4 实战:一个真实的功耗问题排查

我记得有一次,用户反馈手机待机一晚上掉电 15%。我拿到机器后,先看了 Logcat:

D/AlarmManager( 1234): Sending alarm: Alarm{type=2, interval=30000, tag=*walarm*:com.shady.app/.AlarmReceiver}
D/PowerManagerService( 1234): acquireWakeLock: flags=0x1, tag="com.shady.app:BackgroundWork", uid=10456

好家伙,30 秒一个唤醒闹钟,还持有一个 WakeLock 不放。我直接用 adb shell dumpsys package com.shady.app 查了一下,发现是个天气 App。

为什么天气 App 要这么频繁地唤醒系统?说白了,就是它想实时更新天气数据。但 30 秒刷新一次,有必要吗?

我给的建议是:

  1. 把闹钟间隔改成 30 分钟以上。
  2. 使用 setInexactRepeating() 代替 setRepeating(),让系统可以批量处理闹钟。
  3. WakeLock 在后台任务完成后立即释放,不要持有超过 10 秒。
📌 核心要点:
  • WakeLock 日志看 acquirerelease 是否成对出现。
  • AlarmManager 日志看 *walarm* 前缀和 interval 字段。
  • dumpsys 命令可以拿到更详细的信息。
  • 不要依赖超时机制释放 WakeLock,一定要显式释放。

好了,关于 WakeLock 和 AlarmManager 的日志解读,今天就聊到这里。你下次遇到耗电问题,不妨先按这个思路查一遍 Logcat,大概率能找到元凶。


公众号:蓝海资料掘金营,微信deep3321