Android稳定性-51-Android 稳定性问题复盘模板:现象、证据、根因、修复和预防

稳定性问题复盘如果只写“问题现象、原因分析、解决方案”,通常很快就会变成缺陷单的加长版。它可能记录了发生过什么,却没有让团队下次更快发现、更准定位、更早预防。真正有价值的复盘,不是为了证明谁对谁错,而是把一次故障转化为可以复用的工程经验。

Android 稳定性问题尤其需要复盘。因为很多问题不是单点代码错误,而是跨 App、Framework、Native Service、HAL、Kernel、测试环境、版本节奏和监控能力的组合结果。一次 system_server Watchdog,表面是系统卡死,背后可能是锁竞争、Binder 调用、I/O 阻塞、低内存、供应商服务超时和测试场景叠加。复盘如果只停在“修复某处死锁”,下个版本可能还会以另一个形态出现。

这篇给一套可直接使用的问题复盘方法。重点放在现象、证据、根因、修复和预防五个部分,但每个部分都要写到能被别人复查和复用。

一、复盘要先定义边界

复盘开始前,先确认这次复盘到底复盘什么。是复盘一个缺陷,还是一类问题;是复盘技术根因,还是复盘版本决策;是复盘测试漏检,还是复盘线上止损。边界不清,复盘很容易发散成所有人都能补充一句,但没有结论。

建议在复盘文档开头写四个边界:时间边界、版本边界、影响边界、责任边界。时间边界说明问题从什么时候被引入、什么时候被发现、什么时候被修复。版本边界说明影响哪些 build、哪些渠道、哪些机型。影响边界说明用户或测试路径受到什么影响。责任边界说明哪些团队参与复盘,不是为了追责,而是为了确认后续动作谁能落地。

一个清楚的边界示例:

1
2
3
4
5
复盘对象:V6.1.3 灰度阶段锁屏后偶发 system_server Watchdog
时间边界:V6.1.1 引入,V6.1.3 G1 灰度第 7 小时发现,V6.1.4 修复
版本边界:A05/A06,Android 14 分支,公开渠道 G1
影响边界:锁屏后设备无响应并自动重启,影响基础可用性
参与团队:Power Framework、vendor sensor、Kernel、测试平台、版本质量

有了边界,后面的证据和改进才不会漂移。

二、现象要写用户视角和系统视角

稳定性问题的现象至少要写两层。第一层是用户或测试看到的表现:黑屏、卡死、重启、闪退、无响应、耗电、发热、连接失败。第二层是系统记录到的表现:进程 crash、ANR traces、Watchdog、dropbox、tombstone、pstore、binder wait、I/O blocked、thermal throttling。

只写用户视角,无法定位;只写系统视角,别人看不懂影响。两层都写,复盘才能连接质量影响和技术证据。

视角 应写内容 示例
用户视角 操作路径、可见表现、影响后果 锁屏 5 分钟后按电源键无响应,约 60 秒后自动重启
测试视角 场景、脚本、设备、概率 20 台 A05,锁屏唤醒循环 12 小时,2 台复现
系统视角 日志、进程、时间线 Watchdog: PowerManagerService monitor blocked 60s
版本视角 首现版本、关联改动 V6.1.1 合入 sensor suspend 优化后出现

现象写清楚后,复盘读者不需要再翻缺陷系统才能理解问题。

三、证据要能还原时间线

复盘里的证据不是贴几段日志。好的证据能还原问题发生前、发生中、发生后的时间线。

建议时间线至少包含:触发动作、异常前状态、第一条异常日志、系统决策、用户可见表现、恢复或失败结果。时间戳要统一,最好使用设备时间,并说明是否和服务器时间有偏差。

1
2
3
4
5
6
7
14:02:10 测试脚本进入锁屏待机,设备电量 78%,温度 34C
14:07:32 sensor service 收到 suspend 回调,等待 vendor sensor flush
14:07:33 PowerManagerService 进入 goToSleep 流程,持有 mLock
14:07:35 system_server binder 线程出现等待,InputDispatcher 无新响应
14:08:35 Watchdog 判定 PowerManagerService monitor 超时
14:08:36 dropbox 写入 watchdog,设备触发重启
14:09:10 设备重新开机,bootreason=watchdog

这类时间线比单独贴 Watchdog 栈更有价值,因为它能暴露因果顺序。很多复盘失败,就是把最后一条错误当成根因。

四、根因要区分直接原因、触发条件和系统性原因

根因分析最常见的问题,是把直接原因当成全部原因。比如“PowerManagerService 持锁等待 sensor 返回”是直接原因,但为什么会持锁等待,为什么测试前没有发现,为什么灰度才暴露,为什么监控没有提前告警,这些才决定预防动作。

建议根因拆成三层。

第一层是直接技术原因:哪段代码、哪个状态机、哪个资源等待导致异常。第二层是触发条件:什么场景、设备、配置、压力组合让问题出现。第三层是系统性原因:评审、测试、监控、准入、知识库中哪个环节没有提前识别风险。

层级 说明 Watchdog 示例
直接原因 导致故障的技术点 PowerManagerService 持锁等待 vendor sensor flush
触发条件 问题出现所需条件 锁屏待机 + 特定 sensor 固件 + 低温恢复场景
系统性原因 流程或能力缺口 suspend/resume 专项没有覆盖该固件批次

只有三层都写,复盘才能从修 bug 走向防复发。

五、修复方案要说明为什么这样改

复盘中的修复方案不要只贴 commit。要写清楚修复思路、替代方案、风险和验证点。

比如 Watchdog 案例里,可能有几个方案:缩短 vendor sensor 等待时间、把等待移出锁、改成异步回调、在 suspend 前增加状态检查、回滚低功耗优化。最终选择哪一个,要说明原因。如果只是写“已修复锁等待”,别人下次看不出为什么不是简单加 timeout。

一个好的修复描述可以这样写:

1
2
3
修复方案:PowerManagerService 不再在持有全局锁期间同步等待 sensor flush 完成,改为记录 pending 状态并由异步回调更新 suspend 结果。vendor sensor 超过 2s 未返回时记录 error event,但不阻塞系统睡眠主流程。
选择原因:直接缩短 timeout 会降低复现概率但不能消除锁等待;回滚低功耗优化会影响待机功耗;异步化能同时消除 Watchdog 风险并保留功耗收益。
引入风险:sensor 状态上报可能晚于 sleep 流程,需要验证唤醒后的状态一致性。

这种写法能帮助后续维护者理解改动意图。

六、验证要覆盖原场景、相邻场景和反向风险

稳定性问题修复后,不能只复测最初路径。要覆盖三类场景。

原场景验证用于证明问题不再复现。相邻场景验证用于证明同一链路的其他入口没有类似问题。反向风险验证用于证明修复没有引入新的问题。

以锁屏 Watchdog 为例,原场景是锁屏待机唤醒循环;相邻场景包括来电唤醒、闹钟唤醒、指纹解锁、低电量待机、弱网待机;反向风险包括待机功耗是否劣化、sensor 状态是否异常、唤醒耗时是否增加。

验证类型 场景 通过标准
原场景 20 台 x 24h 锁屏唤醒循环 无 Watchdog,无同类 blocked 日志
相邻场景 来电、闹钟、指纹、低电量待机 无唤醒失败和输入无响应
反向风险 待机功耗、sensor 状态、唤醒耗时 不劣于基线

验证结论要写样本量和版本号。不要只写“回归通过”。

七、完整案例:一次 Watchdog 复盘

下面把前面的内容串成一个完整案例。

V6.1.3 灰度第 7 小时,线上监控出现 2 台 A05 设备 watchdog 重启。用户反馈是锁屏后按电源键无响应,随后设备自动重启。灰度比例只有 1%,但 Watchdog 属于基础可用性问题,版本团队立即暂停扩大灰度。

证据收集包括:dropbox watchdog 记录、bugreport、pstore、bootreason、灰度上报事件、最近合入列表。两台设备的 Watchdog 栈都指向 PowerManagerService.monitor(),system_server 主线程持有 power lock,另一个 binder 线程等待 sensor service 返回。vendor log 显示 sensor flush 在特定固件版本上偶发超过 60 秒。

时间线显示,问题不是重启后才出现的随机异常,而是在锁屏流程中先发生 sensor flush 长等待,随后 PowerManagerService 持锁阻塞,最后 Watchdog 触发。关联改动是 V6.1.1 合入的低功耗优化:为了确保 suspend 前 sensor 状态一致,Framework 同步等待 vendor 回调。

根因拆分如下:直接原因是 PowerManagerService 在持有全局锁时同步等待 vendor sensor flush;触发条件是 A05 某批 sensor 固件在低温恢复后 flush 偶发超时;系统性原因是低功耗优化评审时只覆盖了常温待机场景,没有覆盖该固件批次和低温恢复,也没有把 sensor flush 耗时纳入灰度监控。

修复采用异步等待方案,避免阻塞 power 主流程;vendor 侧同时修复 flush 超时。验证分三轮:实验室 30 台 x 48 小时锁屏唤醒循环,低温箱 10 台 x 12 小时恢复测试,G1 灰度 24 小时观察。验证期间无 Watchdog,sensor flush 超时降为 0,待机功耗与上一版本持平。

预防动作包括:新增 suspend/resume 专项用例,覆盖低温恢复和不同 sensor 固件;灰度监控增加 sensor_flush_timeout 事件;Framework 评审规则增加“系统锁内不得等待 vendor 长耗时回调”;案例沉淀到 Watchdog 案例库。

这个复盘的结论不是“某模块修了一个 bug”,而是明确了代码规则、测试覆盖和监控指标三类预防动作。

八、复盘会议要控制讨论结构

复盘会议不要从“谁来解释一下”开始。建议按固定顺序推进:先确认事实,再确认根因,再确认修复,再确认预防。事实阶段不讨论责任,根因阶段不讨论排期,预防阶段不重新争论事实。

会议前,测试 owner 应该准备时间线、证据索引、影响范围和问题状态。研发 owner 准备根因假设、修复方案和风险。版本 owner 准备发布影响和决策记录。质量 owner 准备流程改进建议。

会议结束必须产出 action item,不能只产出共识。每个 action item 要有 owner、截止时间、验收方式。

九、常见误判

第一,把最后报错的模块当成根因。Watchdog 报 PowerManagerService,不代表 PowerManagerService 一定是唯一根因;它可能在等待 vendor、I/O 或 Binder。

第二,把复现困难当成无法分析。低概率问题更需要时间线、版本差异和环境差异,而不是等到稳定复现才开始行动。

第三,把修复合入当成复盘完成。复盘完成的标志是预防动作落地,包括用例、监控、规则或知识沉淀。

第四,把复盘写成追责材料。追责会让信息变少,工程复盘要让事实变多。

第五,只关注技术原因,不看准入和监控。很多线上问题不是没人修,而是发布前没有识别出风险,发布后没有及时止损。

十、检查清单

  • 复盘对象是否有清楚的时间、版本、影响和参与边界。
  • 现象是否同时包含用户视角和系统视角。
  • 证据是否能还原异常前、中、后的时间线。
  • 根因是否区分直接原因、触发条件和系统性原因。
  • 修复方案是否说明选择理由、替代方案和引入风险。
  • 验证是否覆盖原场景、相邻场景和反向风险。
  • 结论是否包含版本号、样本量、设备范围和通过标准。
  • 预防动作是否落到用例、监控、准入、代码规则或案例库。
  • 每个后续动作是否有 owner、截止时间和验收方式。
  • 复盘材料是否能让未参与问题的人读懂并复用。

十一、输出物模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 稳定性问题复盘:<版本 + 场景 + 现象>

## 1. 复盘边界
- 问题编号:<bug / top issue id>
- 影响版本:<build id / fingerprint>
- 发现阶段:<实验室 / 灰度 / 线上 / 售后>
- 影响范围:<机型 / 批次 / 渠道 / 用户路径>
- 参与团队:<team list>

## 2. 现象描述
- 用户视角:<用户看到什么>
- 测试视角:<如何触发,概率多少>
- 系统视角:<Crash / ANR / Watchdog / reboot / resource>

## 3. 时间线
| 时间 | 事件 | 证据 |
| --- | --- | --- |
| | | |

## 4. 证据索引
| 证据 | 路径 | 说明 |
| --- | --- | --- |
| bugreport | | |
| logcat | | |
| traces / tombstone / pstore | | |

## 5. 根因分析
- 直接原因:<技术点>
- 触发条件:<场景和环境>
- 系统性原因:<流程、测试、监控或评审缺口>
- 已排除项:<排除依据>

## 6. 修复方案
- 修复方式:<commit / 配置 / 回滚 / 规避>
- 选择理由:<为什么这样改>
- 引入风险:<可能影响什么>

## 7. 验证结论
| 验证类型 | 样本 | 结果 | 结论 |
| --- | --- | --- | --- |

## 8. 预防动作
| 动作 | Owner | 截止时间 | 验收方式 |
| --- | --- | --- | --- |

十二、把复盘动作转成门禁和用例

复盘里最容易流失的是预防动作。会议上大家都同意“后续加强覆盖”,但一个月后没人知道加强了什么。预防动作必须落到具体载体:自动化用例、准入门禁、日志告警、代码评审规则、平台脚本或案例库条目。

以 Watchdog 复盘为例,预防动作可以这样落地:自动化用例增加锁屏唤醒循环和低温恢复;准入门禁增加 system_server Watchdog 为硬阻断;日志告警增加 sensor flush 超时事件;代码评审规则增加系统锁内禁止等待 vendor 长耗时回调;案例库新增 power-sensor suspend 条目。每一项都有不同 owner,也有不同验收方式。

复盘发现 落地载体 验收方式
锁内等待 vendor 回调 代码评审规则 新增 checklist,模块 owner 审阅
低温恢复未覆盖 自动化专项 CI 或周度长稳包含该场景
灰度无 flush 耗时指标 监控事件 看板能按版本统计超时次数
新人不熟悉 Watchdog 路径 案例库 可通过 watchdog/power/sensor 检索

复盘 owner 应该在问题关闭后一周内检查这些动作是否进入相应系统。没有进入系统的预防动作,通常会在忙碌中消失。

十三、复盘数据口径要统一

复盘经常因为数据口径不一致产生争议。测试说复现 3 次,研发说只看到 1 次;线上说 Crash 率上升,版本说实验室没有异常。很多时候不是谁错了,而是统计窗口、设备范围、版本范围、去重规则不同。

复盘材料里要写清数据口径。比如复现次数是按设备计、按 run 计,还是按用户事件计;Crash 率的分母是活跃设备、启动次数,还是场景次数;重启是否排除了手动重启、低电关机和刷机;ANR 是否只统计目标进程,还是包含系统进程。口径一旦清楚,争议会少很多。

1
2
3
4
5
6
数据口径示例:
复现次数:同一设备同一 run 内同类异常只计 1 次。
样本范围:A05/A06,V6.1.3 G1 灰度,公开渠道,排除工程机。
Crash 率分母:目标 App 启动次数,不使用活跃设备数。
重启统计:排除手动 adb reboot 和 OTA 安装重启,仅统计 bootreason 异常项。
时间窗口:2026-04-18 10:00 至 2026-04-19 10:00,设备本地时间。

统一口径不是为了让数据更好看,而是让复盘结论可比较。下次同类问题发生时,团队才能判断风险是变大还是变小。

十四、复盘质量也要审阅

复盘文档写完后,最好有一次轻量审阅。审阅重点不是文字是否漂亮,而是事实是否完整、证据是否支撑结论、预防动作是否可验收。

可以由质量 owner 或领域专家做 15 分钟审阅,按清单打回不合格复盘。常见打回原因包括:没有时间线、根因只写了直接原因、验证没有样本量、预防动作没有 owner、结论没有版本范围、把猜测写成事实。

审阅项 合格标准
事实 时间、版本、设备、影响范围清楚
证据 日志和结论之间能对应
根因 直接原因、触发条件、系统性原因分开
修复 写明取舍和引入风险
验证 有样本量、场景和通过标准
预防 有 owner、截止时间和验收方式

复盘质量审阅会让文档少一些故事,多一些可复用的工程信息。

十五、复盘要保留已排除路径

很多复盘只记录最终根因,不记录排除过什么。这样会丢掉大量定位经验。实际上,已排除路径对后续同类问题非常有价值。它能告诉别人哪些方向看过、凭什么排除、当时用了什么证据。

比如相机黑屏最终根因在 provider 会话释放,但复盘中也应该记录:已排除 App 权限,因为权限状态正常且多个 App 复现;已排除 SurfaceFlinger 死亡,因为进程未重启且其他 layer 正常;已排除低内存,因为 meminfo 和 lmk 日志无异常。下次遇到类似问题,排查者可以直接复用这些排除方法。

已排除路径要避免写成“不是某模块问题”这种结论式表达。更好的写法是“检查了什么证据,证据显示什么,因此暂时排除什么”。这比简单甩锅更专业,也更容易被其他团队接受。

十六、复盘里的责任不是追责

稳定性复盘必须谈责任,但责任不是追责。责任的含义是:哪个环节可以改变现状,哪个团队可以落实预防动作,哪个角色要确认验收结果。如果复盘把责任理解成找错人,参与者会倾向于少说、晚说、模糊说;如果责任被定义为行动归属,复盘会更容易得到真实信息。

可以把责任分成四类。技术责任是代码、配置、架构或供应商实现的问题归属。验证责任是测试覆盖、样本选择、场景设计的问题归属。监控责任是线上指标、日志字段、告警规则的问题归属。决策责任是准入、灰度、回滚和遗留审批的问题归属。一次事故可能四类责任都有,但每一类的改进动作不同。

责任类型 关注问题 改进动作
技术责任 为什么代码或配置会触发故障 修复、重构、评审规则
验证责任 为什么测试没有提前发现 用例、样本、专项矩阵
监控责任 为什么线上没有及时发现 埋点、日志、告警、看板
决策责任 为什么风险被放大 准入、灰度、回滚机制

复盘文档里写责任时,建议直接写动作归属,而不是写情绪化评价。比如“Power 模块补充锁内等待评审项,owner 张三,4 月 30 日前合入 checklist”,比“Power 模块评审不充分”更有用。

十七、复盘要记录决策过程

很多问题的技术根因很清楚,但事故仍然发生,是因为中间做过一些决策:是否带风险灰度、是否扩大流量、是否暂停发布、是否回滚、是否接受遗留。复盘如果不记录这些决策,团队无法判断决策机制是否需要改进。

决策记录不需要很长,但要写清当时已知信息、选择了什么、为什么这么选、结果如何。比如某个相机 Watch 项进入灰度,当时已知修复验证 3000 次无复现,但仍有 provider warning;团队选择 1% 灰度并增加监控;灰度 12 小时后 warning 上升但失败率未超门槛;24 小时后出现用户黑屏投诉,暂停扩大。这样的记录能帮助团队判断停止条件是否太宽、监控指标是否滞后。

1
2
3
4
5
6
7
8
决策记录模板:
时间:<yyyy-mm-dd hh:mm>
已知信息:<当时掌握的证据>
可选方案:<继续 / 暂停 / 回滚 / 限量 / 补测>
最终选择:<选择及审批人>
选择理由:<为什么>
后续结果:<事实反馈>
复盘判断:<决策合理、信息不足、门槛需调整等>

复盘不是用事后视角苛责当时决策,而是检查当时的信息和规则是否足够支持正确选择。

十八、复盘结论要能进入培训

一篇好的复盘,应该能被拿来培训新人。不是让新人背事故,而是学习问题分析方法。为了做到这一点,复盘结论要有可教学的结构:典型现象、关键证据、错误路径、正确路径、修复取舍、预防规则。

比如 Watchdog 复盘可以转成一页培训材料:看到 Watchdog 不要只看最后栈;先还原 60 秒前时间线;检查 system_server 锁、Binder、I/O、vendor 回调;确认 bootreason 和 dropbox;验证修复时覆盖原场景、相邻场景和反向风险。这样的材料比“某版本发生过一次 Watchdog”更能提升团队能力。

复盘进入培训后,还能暴露文档是否真正清楚。如果讲不明白,通常说明复盘本身还有缺口:可能证据链不完整,可能根因层级混在一起,可能预防动作太抽象。

十九、复盘后的跟踪不能丢

复盘会结束后,真正困难的是跟踪。很多 action item 在会议纪要里写得很好,但没有进入缺陷系统、需求系统或测试平台,几周后就没人再看。建议复盘动作必须有一个统一承载位置,可以是缺陷子任务、质量改进任务、用例变更单或监控需求单。

跟踪时要区分短期动作和长期动作。短期动作包括修复、回归、灰度观察、回滚策略,通常一两个版本内完成。长期动作包括平台能力、工具脚本、专项矩阵、供应商规范,周期更长,但也要有里程碑。复盘 owner 不一定亲自完成所有动作,但要确保每个动作被系统记录、有人接收、可以验收。

动作类型 承载位置 验收方式
修复回归 缺陷单 / Top Issue 修复包和样本验证通过
用例补充 测试用例库 新用例可执行,有通过记录
监控补充 日志平台需求 看板或告警上线
规则更新 准入标准 / 评审 checklist 下个版本评审使用
案例沉淀 案例库 可检索,有标签和 owner

复盘结束两周后,可以做一次轻量回看:哪些动作已完成,哪些延期,延期是否影响当前版本。如果没有这个回看,复盘很容易停留在文档层面。

二十、复盘材料要保留原始证据索引

复盘文档为了可读性,通常只贴关键日志片段。但原始证据不能丢。半年后同类问题再出现,团队需要回看完整 bugreport、traces、pstore、tombstone、测试脚本和设备信息。只保留截图或复制片段,会让历史案例失去复查价值。

建议复盘文档里保留证据索引表,记录文件名、采集时间、设备、版本、用途和存储位置。证据可以放在制品库、日志平台或内部对象存储,但路径要稳定。涉及隐私或用户数据的材料要脱敏,并写明访问权限。

1
2
3
4
5
证据索引示例:
watchdog_bugreport_A05_20260418.zip:复现设备完整 bugreport,用于 system_server 栈和 dropbox。
pstore_A05_20260418.txt:重启现场 pstore,用于确认 bootreason。
run_config_0420.json:测试脚本和场景配置,用于复现路径。
fix_validation.csv:修复后三轮验证结果,用于关闭结论。

原始证据索引让复盘经得起回看,也让案例库能从复盘中提取可靠材料。

二十一、复盘结论要回到版本风险

稳定性复盘最后还要回到版本风险:这个问题是否只影响当前版本,是否已经进入灰度监控,是否需要调整准入标准,是否影响同平台其他机型。很多复盘把技术修复写清了,却没有说明版本后续怎么处理,导致版本 owner 仍然不知道能否继续扩大。

建议在复盘末尾写一段版本影响结论:当前版本是否可继续、遗留风险是什么、观察到什么时候、同平台是否需要排查、下一版是否要补专项。这样复盘不只服务技术团队,也能服务发布决策。

二十二、小结

问题复盘的价值,不在于把一次故障写完整,而在于让团队下次少走弯路。现象要连接用户影响和系统证据,证据要能还原时间线,根因要拆到直接原因、触发条件和系统性原因,修复要说明取舍,预防要有可验收动作。

当复盘能沉淀成用例、监控、准入规则、代码评审规则和案例库条目时,它才真正结束。否则它只是一次写得更长的缺陷关闭说明。