This commit is contained in:
2026-03-01 22:10:59 +08:00
parent 77c826d022
commit 0ce8f409c3
3 changed files with 839 additions and 27 deletions

View File

@@ -0,0 +1,591 @@
# Android WindowManagerService (WMS) 架构深度解析
**版本**: Android 13 (API 33)
**面向对象**: Framework层高级工程师
**作者**: Android系统团队
---
## 1. WMS概述
### 1.1 架构地位与服务初始化
WindowManagerService作为Android图形系统的中枢运行于`system_server`进程管理所有窗口的生命周期、布局、层级与输入事件分发。它与ActivityManagerService(AMS)、SurfaceFlinger构成Android Framework的三驾马车。
**服务启动时序 (SystemServer.java)**:
```java
// frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
// 1. 初始化WMS
wm = WindowManagerService.main(context, inputManager, ...);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
// 2. WMS就绪后通知AMS
wm.displayReady();
wm.systemReady();
}
```
**核心初始化链路**:
- `main()` → 创建`WindowManagerService`实例
- 初始化`WindowManagerPolicy` (策略接口,实际为`PhoneWindowManager`)
- 创建`DisplayContent`管理多显示器
- 关联`InputManagerService`建立输入通道
### 1.2 核心架构类图
```mermaid
classDiagram
class WindowManagerService {
- WindowManagerPolicy mPolicy
- WindowHashMap mWindowMap
- RootWindowContainer mRoot
- H mH
+ addWindow()
+ removeWindow()
+ relayoutWindow()
- performLayoutAndPlaceSurfacesLocked()
}
class WindowState {
- IWindow mClient
- WindowToken mToken
- WindowManager.LayoutParams mAttrs
- Session mSession
- SurfaceControl mSurfaceControl
+ openInputChannel()
+ attach()
}
class WindowToken {
- IBinder token
- int windowType
- DisplayContent mDisplayContent
+ addWindow()
}
class DisplayContent {
- Display mDisplay
- TaskDisplayArea mTaskDisplayArea
- DisplayPolicy mDisplayPolicy
- InputMonitor mInputMonitor
+ getWindowToken()
+ computeImeTarget()
}
class InputMonitor {
- InputWindowHandle mInputWindowHandles
+ updateInputWindowsLw()
+ setInputFocusLw()
}
WindowManagerService --> DisplayContent : 管理
WindowManagerService --> WindowState : 管理
DisplayContent --> WindowToken : 包含
WindowToken --> WindowState : 包含
WindowManagerService --> InputMonitor : 持有
InputMonitor --> WindowState : 监控
```
---
## 2. 窗口管理核心
### 2.1 核心数据结构
| 类名 | 作用 | 关键成员 |
|------|------|----------|
| **WindowState** | 真实窗口状态实体 | mAttrs(窗口参数), mFrame(显示区域), mSurfaceControl(Surface句柄) |
| **WindowToken** | 窗口令牌,同一组窗口的集合 | token(Binder令牌), mChildren(子窗口列表) |
| **DisplayContent** | 逻辑屏幕管理 | mDisplay(物理显示), mTaskDisplayArea(任务区) |
| **ActivityRecord** | Activity对应的窗口Token | 继承自WindowToken绑定AMS生命周期 |
| **Session** | 应用进程与WMS的会话 | 每个应用进程对应一个Session |
### 2.2 窗口添加流程 (`addWindow`)
```mermaid
sequenceDiagram
participant App as 应用进程
participant WMS as WindowManagerService
participant DC as DisplayContent
participant SF as SurfaceFlinger
App->>WMS: Session.addToDisplayAsUser()
activate WMS
WMS->>WMS: 权限校验(mPolicy.checkAddPermission)
WMS->>DC: displayContent.getWindowToken()
alt Token不存在
WMS->>WMS: 创建WindowToken
end
WMS->>WMS: 创建WindowState
WMS->>WMS: displayPolicy.adjustWindowParamsLw()
WMS->>WMS: validateAddingWindowLw()
WMS->>WindowState: openInputChannel()
Note right of WindowState: 创建InputChannel对<br/>一端给WMS一端给应用
WMS->>WMS: win.attach() → 加入容器
WMS->>mWindowMap: put(client.asBinder(), win)
WMS->>WindowToken: addWindow(win)
WMS->>WMS: updateFocusedWindowLocked()
WMS->>DC: getInputMonitor().setInputFocusLw()
WMS->>WindowState: getParent().assignChildLayers()
WMS-->>SF: SurfaceControl.openTransaction()
WMS-->>SF: 创建Surface
WMS-->>SF: SurfaceControl.closeTransaction()
deactivate WMS
```
**源码核心片段 (Android 13)** :
```java
// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, LayoutParams attrs, ...) {
synchronized (mGlobalLock) {
// 1. 重复添加检查
if (mWindowMap.containsKey(client.asBinder())) {
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
// 2. Token获取/创建
WindowToken token = displayContent.getWindowToken(attrs.token);
if (token == null) {
token = new WindowToken.Builder(this, binder, type)
.setDisplayContent(displayContent)
.build();
}
// 3. 创建WindowState
final WindowState win = new WindowState(this, session, client, token,
parentWindow, appOp[0], attrs, viewVisibility, ...);
// 4. 打开输入通道
if (outInputChannel != null) {
win.openInputChannel(outInputChannel);
}
// 5. 加入管理容器
win.attach();
mWindowMap.put(client.asBinder(), win);
win.mToken.addWindow(win);
// 6. 触发布局与焦点更新
performLayoutAndPlaceSurfacesLocked();
}
}
```
### 2.3 窗口删除流程
**主要步骤**:
1. **线程检查**: 确保调用线程与创建线程一致
2. **移除引用**: 从`ViewRootImpl`列表、布局参数列表、View列表中删除
3. **动画处理**: 如果窗口正在运行动画,延迟删除
4. **资源释放**: 释放Surface、关闭InputChannel
```java
// WindowManagerService.removeWindow() 核心逻辑
void removeWindow(Session session, IWindow client) {
synchronized(mGlobalLock) {
WindowState win = mWindowMap.remove(client.asBinder());
if (win == null) return;
// 关闭输入通道
win.removeInputChannel();
// 从Token中移除
win.mToken.removeWindow(win);
// 释放Surface
win.mSurfaceControl.reparent(null);
win.mSurfaceControl = null;
// 重新计算焦点
updateFocusedWindowLocked();
performLayoutAndPlaceSurfacesLocked();
}
}
```
### 2.4 窗口层级管理 (Z-order)
Android窗口分为三大类层级规则如下
| 窗口类型 | 范围 | 层级特点 |
|---------|------|----------|
| **应用窗口** | 1 ~ 99 | 随Activity生命周期由Task组织 |
| **子窗口** | 1000 ~ 1999 | 必须依附于父窗口Z-order在父窗口之上 |
| **系统窗口** | 2000 ~ 2999 | 最高层级如状态栏、输入法、Toast |
**层级分配算法**:
- `assignLayersLocked()` 在每次布局时调用
- 基于`BaseLayer + (type * 10000) + subLayerOffset`计算
- 系统窗口 > 输入法窗口 > 应用窗口 > 壁纸窗口
```java
// frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void assignChildLayers() {
for (int i = 0; i < mChildren.size(); i++) {
WindowContainer child = mChildren.get(i);
child.assignLayer(getPrefix(), ++layer);
}
}
```
---
## 3. 布局与合成机制
### 3.1 布局流程核心方法
`performLayoutAndPlaceSurfacesLocked()` (简称**LAPS**) 是WMS的心脏处理所有窗口位置、大小、可见性的计算。
**调用时机**:
- 窗口添加/删除/更新
- 配置变化(横竖屏切换、多窗口调整)
- 动画更新
- VSync信号到达
**执行流程**:
```java
// 简化版LAPS流程
void performLayoutAndPlaceSurfacesLocked() {
// 1. 布局窗口位置
performLayoutLocked();
// 2. 分配Surface层值
assignLayersLocked();
// 3. 准备Surface (与SurfaceFlinger交互)
for (WindowState win : mWindows) {
if (win.hasSurface() && win.shouldPrepareSurface()) {
win.prepareSurfaceLocked();
}
}
// 4. 提交事务给SurfaceFlinger
mSurfaceControlFactory.getTransaction().apply();
// 5. 更新输入焦点窗口信息
mInputMonitor.updateInputWindowsLw();
}
```
### 3.2 relayoutWindow 核心流程
应用通过`ViewRootImpl.relayoutWindow()`请求WMS重新计算窗口布局并分配Surface。
```mermaid
sequenceDiagram
participant AppThread as UI线程
participant VRI as ViewRootImpl
participant WMS as WindowManagerService
participant SF as SurfaceFlinger
AppThread->>VRI: performTraversals()
VRI->>WMS: relayoutWindow()
activate WMS
WMS->>WMS: 获取mGlobalLock
WMS->>WindowState: 计算新Frame
WMS->>WindowState: 更新可见区域
alt Surface需要重建
WMS->>WindowState: createSurfaceControl()
WMS-->>SF: createSurface()
else Surface无变化
WMS->>WindowState: setPosition/setSize
end
WMS->>WMS: performLayoutAndPlaceSurfaces()
WMS-->>VRI: 返回RelayoutResult
deactivate WMS
VRI->>AppThread: 通知布局完成
VRI->>SF: 队列绘制命令
```
**性能关键点**: `relayoutWindow` 持有`mGlobalLock`,长时间操作会阻塞其他窗口更新,导致丢帧。
### 3.3 与SurfaceFlinger的交互
WMS通过`SurfaceControl`与SurfaceFlinger通信
```java
// WindowState.createSurfaceControl() 核心逻辑
void createSurfaceControl() {
SurfaceControl.Builder b = new SurfaceControl.Builder()
.setName(mAttrs.getTitle().toString())
.setSize(mFrame.width(), mFrame.height())
.setFormat(mAttrs.format)
.setFlags(flags)
.setParent(mToken.getSurfaceControl());
mSurfaceControl = b.build();
// 设置初始属性
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setPosition(mSurfaceControl, mFrame.left, mFrame.top);
t.setLayer(mSurfaceControl, mLayer);
t.setAlpha(mSurfaceControl, mAttrs.alpha);
t.apply();
}
```
**关键事务类型**:
- `setPosition` / `setSize`: 更新位置尺寸
- `setLayer`: 设置Z-order
- `setAlpha` / `setMatrix`: 动画相关
- `reparent`: 改变Surface父子关系(用于窗口切换动画)
---
## 4. 输入事件分发机制
### 4.1 完整分发链路
```mermaid
flowchart TD
A[InputReader读取事件] --> B[InputDispatcher派发]
B --> C{InputMonitor筛选窗口}
C -->|焦点窗口| D[获取窗口InputChannel]
C -->|触摸坐标命中测试| E[获取命中窗口]
D --> F[通过socket发送]
E --> F
F --> G[应用进程ViewRootImpl]
G --> H[InputEventReceiver接收]
H --> I[ViewRootImpl处理]
I --> J[View树分发]
```
### 4.2 InputMonitor角色
`InputMonitor`是WMS中的核心组件负责维护输入窗口信息并传递给InputDispatcher
```java
// frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
void updateInputWindowsLw(boolean force) {
// 遍历所有窗口,收集输入窗口信息
mDisplayContent.forAllWindows((w) -> {
InputWindowHandle handle = w.getInputWindowHandle();
handle.setTouchableRegion(w.getTouchableRegion());
handle.setFocus(w == mDisplayContent.mCurrentFocus);
handle.setLayer(w.getLayer());
// 添加到列表
mInputWindowHandles.add(handle);
}, true);
// 提交给InputDispatcher
mInputManager.setInputWindows(mInputWindowHandles);
}
```
### 4.3 InputChannel建立
每个窗口在添加时通过`openInputChannel`创建一对Socket连接
```java
// WindowState.openInputChannel()
void openInputChannel(InputChannel outInputChannel) {
String name = getName();
InputChannel[] channels = InputChannel.openInputChannelPair(name);
// 服务端注册到InputDispatcher
mInputWindowHandle = new InputWindowHandle(this);
mService.mInputManager.registerInputChannel(channels[0], mInputWindowHandle);
// 客户端通道通过Binder返回
channels[1].transferTo(outInputChannel);
// 保存服务端通道
mClientChannel = channels[0];
}
```
---
## 5. 跨服务协作机制
### 5.1 WMS与AMS协作
AMS与WMS通过`ActivityRecord`(继承自`WindowToken`)建立绑定关系:
```mermaid
sequenceDiagram
participant AMS
participant WMS
participant Activity as ActivityRecord
AMS->>WMS: setAppWindowToken(token, activity)
Note right of WMS: 绑定Activity与WindowToken
AMS->>AMS: startActivityLocked()
AMS->>WMS: setAppStartingWindow()
WMS-->>Activity: 创建启动窗口
AMS->>WMS: setAppVisibility(visible)
WMS->>Activity: 显示/隐藏Activity窗口
AMS->>WMS: moveTaskToFront()
WMS->>Activity: 调整Task层值
AMS->>WMS: setFocusedApp(token)
WMS->>Activity: 设置焦点窗口
```
**关键交互场景**:
1. **Activity启动**: AMS通知WMS创建`AppWindowToken`
2. **窗口可见性**: AMS控制生命周期WMS控制显示/隐藏
3. **Configuration变化**: 横竖屏切换需两服务协同
4. **Task管理**: AMS维护Task栈WMS维护显示层级
### 5.2 WMS与InputManagerService
```java
// WMS持有IMS引用IMS回调WMS
class WindowManagerService {
InputManagerService mInputManager;
// IMS回调WMS处理注入事件
boolean injectInputEvent(InputEvent event, int mode) {
return mInputManager.injectInputEvent(event, mode);
}
}
```
### 5.3 容器遍历机制 (Android 13新特性)
WMS基于`WindowContainer`树形结构管理提供统一的遍历API
```java
// 遍历所有叶子Task执行操作
void forAllLeafTasks(Consumer<Task> callback) {
mRoot.forAllLeafTasks(callback, true);
}
// 遍历所有Activity
void forAllActivities(Consumer<ActivityRecord> callback) {
mRoot.forAllActivities(callback, true);
}
// 实际应用pause所有后台Task
void pauseBackTasks() {
mRoot.forAllLeafTasks((task) -> {
if (task.isVisible()) task.startPausing();
}, false);
}
```
---
## 6. 性能优化与问题排查
### 6.1 锁竞争优化
WMS重度依赖`mGlobalLock`,优化策略:
| 问题 | 优化方案 |
|------|----------|
| 长时间持有锁 | 拆分锁粒度,`DisplayContent`独立锁 |
| 布局计算耗时 | 异步布局预览 |
| Surface创建阻塞 | 预创建Surface池复用缓冲区 |
| Binder调用同步 | 增加异步接口,如`relayoutAsync` |
**典型案例**: ANR日志分析
```
"android.fg" Blocked -> "JobScheduler" Blocked -> "WMS.relayoutWindow" Blocked
```
→ 表明`relayoutWindow`持有锁时间过长,导致前台线程阻塞
### 6.2 常见异常及解决方案
| 异常 | 触发条件 | 排查方向 |
|------|----------|----------|
| `BadTokenException` | 窗口Token无效 | 检查Activity是否已销毁WindowToken是否正确传递 |
| 窗口闪烁 | Surface频繁重建 | 检查`relayoutWindow`调用频率View层级是否稳定 |
| 触摸事件错乱 | InputChannel未正确关闭 | 使用`dumpsys input`查看窗口注册状态 |
| 多窗口卡顿 | Z-order计算复杂 | 简化窗口层级,减少重叠窗口数量 |
### 6.3 调试命令
```bash
# 查看所有窗口状态
adb shell dumpsys window windows
# 查看输入系统状态
adb shell dumpsys input
# 查看显示信息
adb shell dumpsys display
# 查看WMS内部锁状态
adb shell dumpsys window traces
# 开启WMS详细日志
adb shell setprop log.tag.WindowManager VERBOSE
adb logcat -s WindowManager
```
**关键输出解读**:
```
Window #7 Window{...}:
mDisplayId=0
mSession=Session{...}
mClient=android.view.IWindow$Stub$Proxy
mSurfaceControl=Surface(name=...)
mFrame=[0,0][1080,1920]
mAttrs=WM.LayoutParams{... flags=0x... type=TYPE_BASE_APPLICATION}
```
---
## 7. 附录
### 7.1 核心源码路径
| 组件 | 源码路径 |
|------|----------|
| WMS主类 | `frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java` |
| WindowState | `frameworks/base/services/core/java/com/android/server/wm/WindowState.java` |
| DisplayContent | `frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java` |
| InputMonitor | `frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java` |
| RootWindowContainer | `frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java` |
| ActivityRecord | `frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java` |
### 7.2 版本演进特性
| Android版本 | WMS关键变化 |
|-------------|-------------|
| **Android 10** | 引入`TaskFragment`,支持更细粒度窗口组织 |
| **Android 11** | 窗口同步机制优化减少Surface重建 |
| **Android 12** | 新增`WindowInsets`动画,改进多窗口布局 |
| **Android 13** | 容器遍历API标准化锁粒度细化 |
### 7.3 推荐文献
1. 《深入理解Android内核设计思想》- 窗口系统章节
2. AOSP官方文档: [source.android.com/docs/core/graphics](https://source.android.com/docs/core/graphics)
3. 老罗的Android之旅: WMS系列博客
---
## 总结
WindowManagerService作为Android窗口系统的中枢承担着窗口生命周期管理、布局计算、输入事件路由三大核心职责。理解WMS需要把握以下几点
1. **架构分层**: 客户端(WindowManager) ↔ 服务端(WMS) ↔ 底层(SurfaceFlinger)的三层模型
2. **核心对象**: WindowState(窗口实体)、WindowToken(逻辑分组)、DisplayContent(显示区域)
3. **关键流程**: addWindow/removeWindow/relayoutWindow 构成窗口生命周期
4. **锁机制**: `mGlobalLock`是性能关键点,异步化是优化方向
5. **协同机制**: AMS控制生命周期WMS控制显示SurfaceFlinger负责合成
掌握WMS原理不仅是解决窗口相关问题的基石更是深入理解Android图形系统、输入系统的必备知识。

View File

@@ -0,0 +1,235 @@
好的遵照您的指示。作为一名高级Android开发工程师我将基于您提供的详细规范为您撰写一份关于“Android整机性能分析与问题定位”的实战指南。这份指南将严格遵循您设定的技术背景、流程规范、质量标准和交付要求。
---
# 版本历史
| 版本 | 日期 | 作者 | 变更描述 |
| :--- | :--- | :--- | :--- |
| 1.0 | 2024-05-24 | AI Assistant | 初始版本,根据高级开发工程师规范制定 |
## 一、引言
本指南旨在为高级Android开发工程师提供一个系统化、标准化的整机性能分析框架。当面对海量性能日志和Trace数据时工程师可依据此文档进行高效、精准的问题定位并产出高质量的根因分析与优化建议。
## 二、分析准备:数据预处理与验证
在开始深入分析前,必须确保输入数据的完整性和一致性。这是所有后续分析的基石。
### 2.1 数据完整性检查清单
收到测试团队的bugreport或日志包后请使用自动化脚本或手动验证以下核心文件是否存在且内容有效
```bash
# 示例脚本片段: check_integrity.sh
#!/bin/bash
BUGREPORT_DIR=$1
echo "正在验证数据完整性于: $BUGREPORT_DIR"
# 必须文件列表
REQUIRED_FILES=(
"bugreport-*.txt" # 主bugreport报告
"logcat.txt" # 完整logcat
"kernel.log" | "dmesg.txt" # 内核日志
"traces.txt" # ANR traces
"perfetto-trace.perfetto-trace" | "systrace.html" # Perfetto或Systrace
"dumpstate_board.txt" # 板级信息
)
for pattern in "${REQUIRED_FILES[@]}"; do
if ! ls $BUGREPORT_DIR/$pattern 1> /dev/null 2>&1; then
echo "警告: 未找到匹配模式 '$pattern' 的文件。"
else
echo "✓ 找到: $(ls $BUGREPORT_DIR/$pattern | head -n 1)"
fi
done
```
### 2.2 时间同步与归一化
不同日志源(如`logcat``kernel`)可能使用不同的时间基准。为统一分析,需将所有日志时间戳转换为相对时间(例如,相对于开机时间或第一个关键事件的时间)。
- **kernel (dmesg)**:通常为`[ 秒.微秒]`格式,表示系统启动后的时间。
- **logcat**可显示绝对时间或相对时间。在bugreport中通常包含绝对时间。
**最佳实践**找到第一个ANR或关键事件在`logcat`中的绝对时间,然后在`dmesg`中找到对应时间点的内核日志,以建立时间关联。
## 三、系统性分析流程(黄金四步法)
按照“由粗到细、由表及里”的原则,分步执行分析。
### 3.1 第一步:初步症状定位与关键事件捕获 (5-10分钟)
目标是快速识别问题的表象、发生时间和影响的进程。
1. **快速扫描ANR与崩溃**
```bash
# 扫描logcat.txt
grep -n "ANR in" logcat.txt --color=always
grep -n "FATAL EXCEPTION" logcat.txt --color=always
grep -n "am_anr" logcat.txt # ActivityManagerService的ANR事件日志
```
2. **定位UI卡顿**
```bash
# 查找Choreographer丢帧记录通常以"Choreographer"或"Skipped X frames"开头
grep -n "Choreographer.*Skipped" logcat.txt --color=always
```
3. **检查系统关键错误**
```bash
# 内核错误、低内存、服务死亡等
grep -n "cancelled due to low memory" logcat.txt
grep -ni "service.*died" logcat.txt
grep -n "Out of memory" kernel.log
```
4. **记录关键信息**
- **问题进程名与PID**`com.example.app` (PID: 1234)
- **发生时间戳**`2024-05-24 10:30:15.123`
- **问题类型**`ANR (Input dispatching timed out)` 或 `UI Jank`
### 3.2 第二步:系统资源瓶颈分析 (15-30分钟)
判断问题是否由系统全局资源CPU、内存、IO紧张引起。
#### 3.2.1 CPU调度分析
- **工具**Perfetto UI (ui.perfetto.dev) 或 `systrace`。
- **操作**加载trace文件。
- **关注点**
1. **整体负载**查看CPU Frequency和Core C-State图是否存在长时间满频运行或核心被限频
2. **关键进程调度**找到问题进程PID的“Slices”轨道。分析其在ANR/卡顿期间的调度情况:
- **Running**:绿色,表示正在执行。如果占比高,说明它在忙于自己的计算。
- **Runnable**蓝色或深色表示它想运行但CPU被其他任务抢占。这指向**CPU资源竞争**。
- **Sleep/Uninterruptible Sleep**灰色表示它在等待锁、IO、Binder。这指向**锁竞争或IO阻塞**。
3. **寻找“大胃王”**按CPU占用排序找出在问题时间段内抢占CPU的高负载进程如媒体编解码、后台压缩任务
#### 3.2.2 内存压力检测
- **工具**`bugreport.txt` 中的 `meminfo` 部分,`dumpsys meminfo`,以及 `kernel.log`。
- **操作**:在`bugreport`中搜索`Total RAM`和`Free Memory`。
- **关注点**
1. **可用内存低**`MemFree` + `Cached` 是否远低于总内存的20%
2. **Low Memory Killer(LMK) 日志**:在 `kernel.log` 或 `logcat` 中搜索 `lowmemorykiller` 或 `lmk`。频繁的LMK杀进程是内存严重不足的铁证。
```
[ pid= 937, uid= 123] (com.android.systemui) 杀进程以回收内存
```
3. **Zone Normal 水位**:在`dmesg`中查看`zoneinfo`,判断`normal` zone是否频繁进入`min`水位以下触发直接或kswapd内存回收。内存回收尤其是直接回收是巨大的性能开销。
#### 3.2.3 I/O阻塞分析
- **工具**Perfetto (添加`f2fs`/`ext4` 和 `block` 数据源)。
- **操作**在Perfetto UI中放大问题时间窗口。
- **关注点**
1. **进程状态为Uninterruptible Sleep (D状态)**在进程的“State”轨道中如果大量出现灰色且工具提示为 `D`表示进程正在执行不可中断的I/O等待。
2. **Binder事务延迟**查看Binder相关trace事件`binder_transaction` `binder_reply`。在问题进程的Binder线程中是否存在长时间未完成的Binder调用调用方和被调用方分别是谁
3. **I/O操作**:打开`ftrace`事件中的`f2fs/iostat`或`block_rq_issue`,查看是否有大量或超大的块读写请求发生。
### 3.3 第三步:进程内细粒度分析 (30-60分钟)
在排除或确认系统瓶颈后,深入分析问题进程自身的执行流。
#### 3.3.1 ANR根因分析
- **工具**`traces.txt` 文件Perfetto。
- **操作**
1. 在`traces.txt`中找到发生ANR的进程及其主线程的堆栈。
2. **关键时间线分析 (P0要求)**结合Perfetto精确到微秒级重建事件序列
- **T0**: Input事件发送到system_server。
- **T1**: system_server将该事件分发给目标应用进程通过Binder
- **T2**: 目标应用主线程收到Binder调用开始处理。
- **T3 (ANR触发点)**: 5秒后system_server未收到处理完成的信号输出ANR。
3. **调用栈深度分析 (P0要求)**`traces.txt`中主线程的栈顶往往就是“罪魁祸首”。
- **常见情况**
- **锁等待**`waiting to lock <0x...>`,表明主线程在等待一个被其他线程持有的锁。需找到持锁线程的堆栈。
- **Binder调用**正在执行一个Binder同步调用等待其他进程通常是system_server返回。需分析被调用端的耗时。
- **自身耗时**在应用代码或某系统库函数中循环如复杂的布局、Json解析、数据库查询。
#### 3.3.2 UI Jank/渲染卡顿分析
- **工具**Perfetto (启用`vsync` `gfx` `input`)。
- **操作**
1. 找到`SurfaceFlinger`和应用的`RenderThread`轨道。
2. 分析一帧的完整流水线:
- **App主线程 (UI Thread)**处理Input执行`onMeasure` `onLayout` `onDraw`。耗时过长会导致帧错过vsync。
- **RenderThread**将DisplayList绘制指令提交给GPU。耗时可能由复杂绘制指令引起。
- **hwui**等待GPU完成。可能由GPU负载过高或驱动问题引起。
- **SurfaceFlinger**合成各应用图层。如果此阶段耗时可能是合成层数过多Overdraw或硬件合成器(HWC)能力不足。
3. **定位最慢阶段**:哪一阶段的彩色条形图最长?哪个阶段导致了帧截止时间(红色垂直线)的错过?
### 3.4 第四步:关联分析与根因确认 (15分钟)
将以上各步骤的发现关联起来,形成完整证据链。
- **案例**初步定位有ANR第二步发现内存紧张LMK频繁杀进程。第三步发现被杀进程恰好是ANR进程依赖的一个关键服务如ContentProvider所在进程。那么**根本原因**就不是主线程阻塞,而是**系统内存不足导致依赖服务被回收**从而造成调用方ANR。
- **验证**查看ANR发生前`logcat`中是否有`lmk`杀掉关键服务的日志?查看`traces.txt`中主线程是否在`binderTransaction`等待一个不存在的进程返回?
## 四、问题分类与优先级判定
根据分析结果,对问题进行定级。
- **P0级 (阻塞性)**
- **标准**ANR持续时间 > 5秒或连续丢帧 > 16帧。
- **示例**任何用户可见的ANR启动应用时黑屏/白屏超过5秒滑动列表持续卡顿。
- **P1级 (严重)**
- **标准**UI响应延迟 > 100ms或内存泄漏 > 50MB或发生系统服务重启。
- **示例**点击按钮后200ms才有反应后台长期占用大量内存导致其他应用被LMKsystem_server或surfaceflinger重启。
- **P2级 (一般)**
- **标准**:单次操作偶尔延迟,或资源占用在极端场景下才出现问题。
- **示例**:首次打开相册慢;在某些特定网络条件下,列表图片加载慢。
## 五、交付成果与质量标准
### 5.1 核心交付物:性能分析报告 (示例模板)
```markdown
## 问题概述
- **现象**:在视频播放界面快速滑动评论区时,界面严重卡顿,帧率降至个位数。
- **影响范围**:视频类应用 `com.media.video` (PID: 9876) Android 12 设备。
- **P等级**P1 (严重 UI Jank)
## 根因分析
### 关键时间线
在Perfetto trace的时间窗口 `12:05:30.000` - `12:05:32.000` 内:
1. **UI Thread (TID 9876)**:在 `RecyclerView` 的 `onBindViewHolder` 中调用了 `MediaMetadataUtils.getAlbumArt()`,耗时约 **45ms**。
2. **RenderThread (TID 9882)**:主线程耗时导致错过了 `Choreographer` 的 `doFrame` 回调,无法开始下一帧的绘制。
3. **后续帧**UI Thread 在多个连续的 `doFrame` 中均耗时 > 30ms导致连续丢帧超过30帧。
*(此处应插入Perfetto标记帧超时的截图)*
### 调用栈深度分析
通过 `simpleperf` 对主线程采样,发现 `getAlbumArt()` 中的耗时操作最终调用到了 `BitmapFactory.decodeStream()` 从本地文件系统加载一张较大分辨率的图片。
*(此处应插入调用栈火焰图或堆栈列表)*
- **层1 (应用层)**`com.media.video.adapter.CommentAdapter.onBindViewHolder`
- **层2 (应用层)**`com.media.video.utils.MediaMetadataUtils.getAlbumArt`
- **层3 (Framework)**`android.graphics.BitmapFactory.decodeStream`
- **层4 (Native)**`libskia.so` 中的 `SkCodec::getPixels`,显示正在解码一张 JPEG 图片。
- **根因**`RecyclerView` 滑动时主线程同步解码磁盘上的高清图片阻塞了UI绘制。
## 优化建议
### 代码修改建议
1. **异步加载**:使用 `Glide` 或 `Coil` 等图片加载库,在后台线程异步加载评论区的用户头像和缩略图。
```kotlin
// 原代码 (可能导致卡顿)
// val bitmap = MediaMetadataUtils.getAlbumArt(context, mediaId)
// holder.albumArt.setImageBitmap(bitmap)
// 优化后代码 (使用Coil)
holder.albumArt.load(MediaMetadataUtils.getAlbumArtUri(mediaId)) {
crossfade(true)
placeholder(R.drawable.placeholder)
}
```
2. **图片压缩**:如果必须直接解码,确保使用 `BitmapFactory.Options` 进行采样,降低加载到内存中的图片尺寸,减少解码时间。
### 配置调整方案
- 无。此问题主要源于应用代码,无需系统配置调整。
## 支持证据
1. `perfetto_trace_jank.html` (已标记问题区间)
2. `trace_ui_thread_jank.txt` (主线程堆栈片段)
3. `cpu_profile.data` (Simpleperf性能采样数据)
```
### 5.2 可复现分析脚本
创建自动化脚本来提取关键指标,例如:
- `extract_anr_traces.py`:自动解析`traces.txt`和`logcat`生成ANR摘要报告。
- `perfetto_metric.sh`调用Perfetto命令行工具计算指定时间段内的平均帧率、Jank次数。
### 5.3 质量检查清单
在提交报告前,使用以下清单进行自查:
- [x] **P0/P1问题全覆盖**所有ANR、严重卡顿均有关联分析。
- [x] **根因精确**:是否定位到具体函数/代码行/系统配置?(例如:`com.example.app$MyClass.onClick (line: 250)` 而不是“主线程阻塞”)
- [x] **证据链完整**是否从现象、日志、trace到代码修改建议形成了逻辑闭环
- [x] **数据量化**优化前后的性能对比是否有数据支撑例如帧率从15fps提升到55fps内存占用降低80MB
- [x] **报告规范**报告格式是否符合Markdown模板要求关键部分是否包含截图
- [x] **可复现性**分析脚本是否能在Linux/macOS环境顺利运行依赖是否清晰