第2章 导航操作场景收集

导航操作是地图类应用核心用户路径,涉及 Fragment/Activity 跳转、地图渲染、路径规划、手势滑动 四大高频场景。每个场景都可能因主线程阻塞、资源竞争或同步调用不当引发 ANR。本章按“现象识别 → 数据采集 → 原因定位 → 修复动作 → 复测 → 预防”六步法,建立场景化排查知识库。

导航操作ANR场景排查流程总览 现象识别 数据采集 原因定位 修复动作 复测 预防 场景现象 跳转无响应 / 地图白屏 路径计算卡顿 / 滑动丢帧 采集手段 Trace / Logcat Systrace / Profiler 根因猜测 主线程IO / 锁等待 渲染耗时 / 计算超时 优化策略 异步加载 / 预渲染 线程池 / 缓存 验证方法 重复操作 + Trace 自动压测 规范 编码规范 监控告警 四大场景 Fragment/Activity跳转 地图渲染 路径规划 手势滑动

1. 现象识别

场景典型现象用户感知
Fragment/Activity 跳转点击按钮或列表项后界面卡住超过5秒;返回键无效“点击没反应”
地图渲染地图区域显示为白屏或灰色网格,缩放拖动不刷新“地图死了”
路径规划输入起终点后“计算中”持续10秒以上,弹窗ANR“路线算不出来”
手势滑动单指滑动、双指缩放时画面滞后、掉帧,最终弹出ANR“地图越划越卡”
判别技巧: 在Logcat中搜索 Input dispatching timed out 或 ANR in 关键字,结合时间戳判断具体触发场景。

2. 数据采集

每个场景需采集的数据维度略有差异,下表列出推荐采集工具与关键指标:

场景必采数据采集命令/方式
Fragment/Activity 跳转主线程堆栈、onCreate/onResume 耗时
adb shell am start -W <package>/<activity>
+ Trace
地图渲染渲染帧时间、GL线程堆栈、图层合成耗时
adb shell dumpsys gfxinfo
+ Systrace
路径规划核心算法调用链、HTTP请求耗时、结果解析耗时第三方Profiler(如Perfetto)
手势滑动Touch事件分发路径、Vsync信号间隔、动画回调堆栈Screen Record + Trace
采集陷阱: 不要只抓一次Trace;应连续触发3~5次ANR场景,记录条件(网络状态、地图级别、路径点数等),避免偶发问题遗漏。

3. 原因定位

使用故障树(Fault Tree)逻辑逐层排查。以下为各场景的根因排查表:

场景一级判断二级判断典型根因
跳转ANR主线程是否有长时间IOSharePreference写入?DB查询?SP自动commit或DB查询未异步
地图白屏SurfaceView/TextureView是否就绪GL上下文创建是否同步View创建时阻塞等待渲染线程锁
路径规划卡死计算线程是否持有主线程锁结果回调是否切回主线程Handler.post / runOnUiThread 在计算任务未完成时被同步调用
滑动丢帧ANRTouch事件处理中是否有重量级操作onTouchEvent 内是否有 inflate / decodeResource滑动中加载大图或布局引起主线程GC
定位口诀: 主线程堵 → 看堆栈;锁冲突 → 看同步;渲染卡 → 看帧时间;计算慢 → 看线程池。

4. 修复动作

针对各场景的典型优化方案(非全部,仅展示核心修复):

  • 跳转优化: 将 onCreate 中的IO操作(读文件、查DB)异步到子线程;使用 AsyncLayoutInflater 预加载布局。
  • 地图渲染优化: 地图View采用懒加载,首帧只渲染可见区域;对瓦片下载使用LRU缓存 + 预取策略。
  • 路径规划优化: 将路径请求放到独立线程池,设置超时回调;结果解析在子线程完成,最后通过 Handler 投递到主线程。
  • 手势滑动优化: 在手势回调中禁止执行inflate/解码等耗时操作;使用 ViewPropertyAnimator 而非自定义动画循环。

5. 复测

修复后必须在 “同机型、同网络、同操作路径” 下复现测试。复测清单:

  1. 重复触发各场景10次以上,确认无ANR弹出。
  2. 使用 adb shell dumpsys cpuinfo 观察主线程CPU占用是否恢复正常。
  3. 运行自动压测脚本,模拟用户连续导航操作30分钟。
  4. 对比修复前后的Trace文件,确认关键路径耗时降低。

6. 预防

  • 编码规范: 禁止在主线程执行任何网络请求、文件IO、复杂渲染计算;建立 @UiThread / @WorkerThread 注解检查。
  • 监控预警: 集成BlockCanary或自定义ANR监控,上报主线程卡顿超过2秒的堆栈。
  • 灰度验证: 导航模块改动上线前,先在内部体验版收集1天卡顿日志。
养成习惯: 每次提交导航相关代码前,手动执行一遍“跳转→地图→规划→滑动”的全链路操作,并开启Profiler录制,做到“不卡不提交”。