第3章 使用 dumpsys 分析 AMS 状态:命令格式、关键字段解读、活动堆栈与进程信息
3.1 dumpsys 命令结构
dumpsys 是 Android 系统提供的诊断工具,能够直接访问系统服务内部状态。对于AMS性能调优,dumpsys activity 是分析 Activity 管理、任务栈、进程优先级和广播/服务超时的核心入口。
性能诊断第一定律:永远不要猜测问题根源。先用 dumpsys 采集现场数据,再定位具体模块。这能避免 80% 的无意义代码修改。
基础命令格式:
adb shell dumpsys activity [options]
常用 options 说明表:
| 选项 | 输出内容 | 调优场景 |
|---|---|---|
activities | 所有 ActivityRecord、任务栈最详细状态 | 启动速度、窗口焦点、泄露分析 |
processes | 进程 pid、adj 类型/分数、LRU 顺序 | OOM 优化、进程保活、后台耗电 |
services | 已注册及运行中的 ServiceRecord | ANR 定位、服务泄漏、内存占用 |
o | 前台广播接收器、前台服务概览 | 快速排查卡顿与高负载原因 |
broadcasts | 广播队列、历史广播、超时记录 | 广播风暴、有序广播 ANR |
all | 以上所有信息的汇总 | 全量现场快照 |
实战筛选技巧:直接执行 adb shell dumpsys activity 输出可能高达数百行。性能调优时请优先将输出保存到本地文件,再利用 grep 定向过滤。例如:
adb shell dumpsys activity all > ams_dump.txt
grep -E "Stack|Task|mResumed|mFocused|mLastPaused" ams_dump.txt
3.2 关键字段深度解读
以下字段直接决定系统流畅度与稳定性,调优过程中必须能够快速识别。
当前焦点与可见状态
mResumedActivity: ActivityRecord{xxxxx u0 com.example/.MainActivity}
mFocusedActivity: ActivityRecord{xxxxx u0 com.example/.MainActivity}
mLastPausedActivity: ActivityRecord{yyyyy u0 com.example/.SplashActivity}
mResumedActivity:当前正在与用户交互的 Activity,必须快速响应触摸事件。如果此处长期不切换,可能表明启动流程被卡住。mFocusedActivity:窗口焦点归属,通常与 mResumed 一致。不一致时暗示输入法/弹窗/多窗口抢焦点。mLastPausedActivity:上一个被暂停的 Activity。连续快速切换 Activity 时,此字段可以帮助识别是否发生了不必要的重建或重绘。
ActivityRecord 核心字段
ActivityRecord{xxxxx u0 com.example/.DetailActivity t123}
intent={act=android.intent.action.VIEW dat=...}
app=ProcessRecord{yyyyy 12345:com.example/10001}
state=STOPPED
visible=false
frontOfTask=false
procIdle=true
state:生命周期状态(INITIALIZING/RESUMED/PAUSED/STOPPED/DESTROYING)。调优时重点关注停在 PAUSED 或 STOPPED 状态时间过长的 Record。app:指向进程 ProcessRecord。如果为 null,表明进程已死亡,属于“僵尸 Activity”,会导致内存泄漏。visible:真正对用户可见。如果 visible 为 false 但 state 为 RESUMED,属于异常场景,可能导致触控和绘制问题。frontOfTask:是否处于任务栈顶。调优时大多数关注的应该是 frontOfTask=true 的 Record。
调优聚焦:进程死亡后 mResumedActivity 指向一个 app=null 的 ActivityRecord 时,系统会等待 5s 超时再重新创建。这是启动卡顿的常见隐性原因。
3.3 活动堆栈层次结构
Android 通过 Stack → Task → ActivityRecord 三级管理窗口。多窗口、画中画和自由窗口模式引入多个 Stack。
层次关系图解:
栈类型与识别:
- Stack #0:全屏应用栈,绝大多数场景下的主运行栈。
- Stack #1:分屏栈(Split-screen),存在时表明系统处于多窗口模式。
- Stack #2:自由窗口栈(Freeform),常见于 Android TV 或平板模式。
- Stack #3:画中画栈(PIP),视频应用悬浮时出现。
3.4 进程信息与 ADJ 算法
通过 dumpsys activity processes 获取进程优先级,直接关联 LMK 杀进程与后台耗电。
ProcessRecord{yyyyy 12345:com.example.app/10001}
...
setAdj=0
setRawAdj=0
curAdj=0
maxAdj=0
appId=10001
pid=12345
thread=android.app.IApplicationThread$Stub$Proxy@xxxxx
lastPss=245MB
lastCachedPss=210MB
cached=false
hasShownUi=true
...
adj 核心评级表:
- adj=0 (TOP):当前前台应用。系统必须全力保障其性能,杀进程时最后考虑。
- adj=200 (VISIBLE):可见但非前台(例如被部分遮挡)。仍属于重要进程。
- adj=500 (PERCEPTIBLE):可感知进程(如播放音乐、后台定位)。
- adj=900 (CACHED):后台缓存进程。LRU 列表最末尾的进程会优先被 LMK 杀死。
- adj=1001 (UNKNOWN_ADJ):未正确分类的进程,属于异常状态。
调优陷阱:如果低端设备频繁出现桌面重启、应用冷启动,请检查 dumpsys 输出中 cached 进程的总数是否长期超过 LMK 水位线。adj=900 的进程堆积过多会导致严重的卡顿和内存压力。
3.5 性能调优实操案例
场景 A:Activity 启动缓慢
adb shell dumpsys activity activities | grep -E "mResumedActivity|mFocusedActivity|mLastPausedActivity"
观察到 mLastPausedActivity 在目标 Activity 启动前长期未更新,说明 PAUSE 阶段卡住。此时应进一步检查目标 Activity 的 onPause() 是否执行了耗时 IO 操作。
场景 B:内存泄漏检测
adb shell dumpsys activity activities | grep -E "app=null|ActivityRecord"
如果发现大量 ActivityRecord 的 app=null 且 state=STOPPED 或 DESTROYING,说明这些 Activity 的进程已被杀死,但 ActivityRecord 仍未从栈中移除,属于典型的泄漏路径。
场景 C:ANR 快速定位
adb shell dumpsys activity processes | grep -E "adj=|setAdj=|curAdj=|lastPss="
关注 setAdj 是否被异常限制在 0/200/500 以外,以及 lastPss 是否突然飙升。配合 dumpsys activity o 查看前台广播接收器和服务,可以迅速定位造成主线程阻塞的第三方组件。
每日调优习惯:每周对主力机型执行一次 dumpsys 全量采集,对比 cached 进程数量与 lastPss 均值,能够提前发现内存衰退趋势,避免线上爆发 OOM 或 ANR 异常。