diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 5ab58f7..751ce22 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -27,30 +27,16 @@ "state": { "type": "markdown", "state": { - "file": "docs/Obsidian笔记体系/Daily/2026-01-13.md", + "file": "docs/学习笔记/Android整机性能分析与问题定位实战指南.md", "mode": "source", "source": false }, "icon": "lucide-file", - "title": "2026-01-13" - } - }, - { - "id": "59894255df52f532", - "type": "leaf", - "state": { - "type": "markdown", - "state": { - "file": "docs/学习笔记/两份提示词对比.md", - "mode": "source", - "source": false - }, - "icon": "lucide-file", - "title": "两份提示词对比" + "title": "Android整机性能分析与问题定位实战指南" } } ], - "currentTab": 2 + "currentTab": 1 } ], "direction": "vertical" @@ -212,19 +198,24 @@ "bases:Create new base": false } }, - "active": "59894255df52f532", + "active": "25c9f7051aac05b3", "lastOpenFiles": [ + "docs/Obsidian笔记体系/Projects/aitsc/ai提示词常用命令.md", + "docs/学习笔记/Android整机性能分析与问题定位实战指南.md", + "docs/学习笔记/Android WindowManagerService (WMS) 架构深度解析(第一次提示词).md", "docs/学习笔记/Android WindowManagerService核心原理深度解析(专家第二次提示词).md", "docs/学习笔记/两份提示词对比.md", - "docs/# Android WindowManagerService (WMS) 架构深度解析(专家第一次提示词).md", - "docs/学习笔记/Android游戏整机性能卡顿与丢帧根因分析与优化指南.md", + "docs/学习笔记/前端学习.md", + "docs/学习笔记/腾讯云相关账户.md", + "docs/学习笔记/效率提升.md", "docs/学习笔记/Android性能分析标准化操作手册.md", - "docs/Obsidian笔记体系/Projects/aitsc/ai提示词常用命令.md", + "docs/Obsidian笔记体系/Daily/2026-01-13.md", + "docs/学习笔记/Android游戏整机性能卡顿与丢帧根因分析与优化指南.md", + "docs/# Android WindowManagerService (WMS) 架构深度解析(专家第一次提示词).md", "docs/Obsidian笔记体系/Projects/知你-调测/知你--调测.md", "docs/Obsidian笔记体系/Projects/aitsc", "docs/Obsidian笔记体系/Daily/2026-01-14.md", "docs/Obsidian笔记体系/Daily/2024-06-02.md", - "docs/Obsidian笔记体系/Daily/2026-01-13.md", "docs/Obsidian笔记体系/Daily/2026-01-15.md", "docs/Obsidian笔记体系/Daily/2026-01-20.md", "docs/学习笔记/高级性能问题分析.md", @@ -236,11 +227,6 @@ "docs/Obsidian笔记体系/Areas/09-调试与工具链/Systrace_Perfetto全解读.md", "docs/学习笔记/php/未命名.md", "docs/git/git设置用户名和邮箱.md", - "docs/学习笔记/产品经理/产品经理--写文档.md", - "docs/学习笔记/产品经理/沟通与表达详解.md", - "docs/学习笔记/产品经理/数据分析详解.md", - "docs/学习笔记/产品经理/产品运营详解.md", - "docs/学习笔记/产品经理/产品设计详解.md", "docs/学习笔记/产品经理", "Pasted image 20260129111501.png", "Pasted image 20260129111451.png", diff --git a/docs/学习笔记/Android WindowManagerService (WMS) 架构深度解析(第一次提示词).md b/docs/学习笔记/Android WindowManagerService (WMS) 架构深度解析(第一次提示词).md new file mode 100644 index 0000000..f89dbda --- /dev/null +++ b/docs/学习笔记/Android WindowManagerService (WMS) 架构深度解析(第一次提示词).md @@ -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对,
一端给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 callback) { + mRoot.forAllLeafTasks(callback, true); +} + +// 遍历所有Activity +void forAllActivities(Consumer 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图形系统、输入系统的必备知识。 \ No newline at end of file diff --git a/docs/学习笔记/Android整机性能分析与问题定位实战指南.md b/docs/学习笔记/Android整机性能分析与问题定位实战指南.md new file mode 100644 index 0000000..f6fe27c --- /dev/null +++ b/docs/学习笔记/Android整机性能分析与问题定位实战指南.md @@ -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才有反应;后台长期占用大量内存导致其他应用被LMK;system_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环境顺利运行?依赖是否清晰? \ No newline at end of file