第2章 导航操作场景收集
导航操作是地图类应用核心用户路径,涉及 Fragment/Activity 跳转、地图渲染、路径规划、手势滑动 四大高频场景。每个场景都可能因主线程阻塞、资源竞争或同步调用不当引发 ANR。本章按“现象识别 → 数据采集 → 原因定位 → 修复动作 → 复测 → 预防”六步法,建立场景化排查知识库。
1. 现象识别
| 场景 | 典型现象 | 用户感知 |
|---|---|---|
| Fragment/Activity 跳转 | 点击按钮或列表项后界面卡住超过5秒;返回键无效 | “点击没反应” |
| 地图渲染 | 地图区域显示为白屏或灰色网格,缩放拖动不刷新 | “地图死了” |
| 路径规划 | 输入起终点后“计算中”持续10秒以上,弹窗ANR | “路线算不出来” |
| 手势滑动 | 单指滑动、双指缩放时画面滞后、掉帧,最终弹出ANR | “地图越划越卡” |
判别技巧: 在Logcat中搜索
Input dispatching timed out 或 ANR in 关键字,结合时间戳判断具体触发场景。2. 数据采集
每个场景需采集的数据维度略有差异,下表列出推荐采集工具与关键指标:
| 场景 | 必采数据 | 采集命令/方式 |
|---|---|---|
| Fragment/Activity 跳转 | 主线程堆栈、onCreate/onResume 耗时 | + Trace |
| 地图渲染 | 渲染帧时间、GL线程堆栈、图层合成耗时 | + Systrace |
| 路径规划 | 核心算法调用链、HTTP请求耗时、结果解析耗时 | 第三方Profiler(如Perfetto) |
| 手势滑动 | Touch事件分发路径、Vsync信号间隔、动画回调堆栈 | Screen Record + Trace |
采集陷阱: 不要只抓一次Trace;应连续触发3~5次ANR场景,记录条件(网络状态、地图级别、路径点数等),避免偶发问题遗漏。
3. 原因定位
使用故障树(Fault Tree)逻辑逐层排查。以下为各场景的根因排查表:
| 场景 | 一级判断 | 二级判断 | 典型根因 |
|---|---|---|---|
| 跳转ANR | 主线程是否有长时间IO | SharePreference写入?DB查询? | SP自动commit或DB查询未异步 |
| 地图白屏 | SurfaceView/TextureView是否就绪 | GL上下文创建是否同步 | View创建时阻塞等待渲染线程锁 |
| 路径规划卡死 | 计算线程是否持有主线程锁 | 结果回调是否切回主线程 | Handler.post / runOnUiThread 在计算任务未完成时被同步调用 |
| 滑动丢帧ANR | Touch事件处理中是否有重量级操作 | onTouchEvent 内是否有 inflate / decodeResource | 滑动中加载大图或布局引起主线程GC |
定位口诀: 主线程堵 → 看堆栈;锁冲突 → 看同步;渲染卡 → 看帧时间;计算慢 → 看线程池。
4. 修复动作
针对各场景的典型优化方案(非全部,仅展示核心修复):
- 跳转优化: 将
onCreate中的IO操作(读文件、查DB)异步到子线程;使用AsyncLayoutInflater预加载布局。 - 地图渲染优化: 地图View采用懒加载,首帧只渲染可见区域;对瓦片下载使用LRU缓存 + 预取策略。
- 路径规划优化: 将路径请求放到独立线程池,设置超时回调;结果解析在子线程完成,最后通过
Handler投递到主线程。 - 手势滑动优化: 在手势回调中禁止执行inflate/解码等耗时操作;使用
ViewPropertyAnimator而非自定义动画循环。
5. 复测
修复后必须在 “同机型、同网络、同操作路径” 下复现测试。复测清单:
- 重复触发各场景10次以上,确认无ANR弹出。
- 使用
adb shell dumpsys cpuinfo观察主线程CPU占用是否恢复正常。 - 运行自动压测脚本,模拟用户连续导航操作30分钟。
- 对比修复前后的Trace文件,确认关键路径耗时降低。
6. 预防
- 编码规范: 禁止在主线程执行任何网络请求、文件IO、复杂渲染计算;建立
@UiThread/@WorkerThread注解检查。 - 监控预警: 集成BlockCanary或自定义ANR监控,上报主线程卡顿超过2秒的堆栈。
- 灰度验证: 导航模块改动上线前,先在内部体验版收集1天卡顿日志。
养成习惯: 每次提交导航相关代码前,手动执行一遍“跳转→地图→规划→滑动”的全链路操作,并开启Profiler录制,做到“不卡不提交”。