常见ANR 案例

179 阅读3分钟

该网页是 Android ANR 系列的第三篇,主要分享了多个 ANR 案例,涵盖死锁、冻结、广播超时等常见问题场景,旨在通过实际案例帮助开发者理解 ANR 的成因与分析方法。以下是详细总结:

一、ANR 常见原因分类

  1. 应用自身问题

    • 死锁:主线程等待锁、调用join()/sleep()/wait()等方法。
    • 主线程阻塞:耗时操作(如复杂布局、IO 读写、数据库操作)、子线程同步锁阻塞。
    • 生命周期超时:ContentProvider、Service、广播接收器的回调方法执行超时。
    • 渲染问题:渲染线程(RenderThread)耗时导致主线程等待。
  2. 系统或远端进程问题

    • Binder 通信耗时:与 SystemServer 通信时服务端处理缓慢或锁竞争。
    • 资源竞争:整机低内存、CPU/IO 负载高、SurfaceFlinger 超时。
    • 窗口异常:焦点窗口缺失或 Input 事件分发异常。

二、典型 ANR 案例解析

1. 死锁案例:今日头条分屏操作 ANR

  • 场景:频繁分屏操作后应用卡死,4-5 秒后恢复。

  • 日志关键信息

    • 主线程(tid=1)阻塞于等待LogReaper锁,该锁被线程 34 持有。
    • 线程 34(LogReaper)等待AtomicInteger锁,形成死锁。
  • 结论:应用内多线程锁竞争导致主线程阻塞。

2. 冻结案例:天气应用输入事件超时

  • 场景:按下返回键后应用无响应,触发输入 ANR。

  • 日志关键信息

    • 系统因灭屏(LcdOff)冻结应用(00:51:04),5 秒后输入事件(KEYCODE_BACK 的 ACTION_UP)处理超时。
    • 解冻日志(xxxHansManager: unfreeze)显示应用被系统功耗优化机制冻结。
  • 结论:系统冻结机制导致应用无法响应输入事件,需检查冻结逻辑是否异常。

3. 广播超时案例:数据库升级 ANR

  • 场景:迁移数据时广播接收器触发 ANR。

  • 关键分析

    • 广播接收器onReceive中使用goAsync()开启子线程处理数据库升级,但未在超时内调用PendingResult.finish()
    • 官方文档说明:goAsync()仅允许异步处理,但广播总耗时(含子线程)仍受超时限制(前台广播 10 秒,后台 30 秒)。
  • 结论:异步处理未及时结束广播,导致超时。

4. 输入 ANR 案例:Launcher 渲染阻塞

  • 场景:Launcher 输入事件分发超时。

  • 日志关键信息

    • 主线程阻塞于等待渲染线程(RenderThread)完成dequeueBuffer操作。
    • 渲染线程卡在与 SurfaceFlinger 的 Binder 通信,因 Buffer 队列满(无可用 Buffer)导致等待。
    • SurfaceFlinger 日志提示 “acquireBuffer: max acquired buffer count reached”。
  • 结论:SurfaceFlinger Buffer 资源不足,导致渲染阻塞,需优化图形资源管理。

5. 其他异常案例

  • 主线程数据库读写:直接在主线程执行 SQLite 操作,如SQLiteOpenHelper.getWritableDatabase()阻塞。
  • Binder 数据量过大:Binder 通信传输数据超过阈值(如 388KB),触发Unreasonably large binder reply buffer异常。
  • Binder 通信失败:内核日志显示 Binder 事务失败(如错误码 - 3、-22),可能因驱动或内存问题导致。

三、ANR 分析与治理思路

  1. 分析流程

    • 通过am_anr定位 ANR 时间点,结合ANR in日志分析 CPU / 内存负载。
    • 解析traces.txt线程堆栈,识别阻塞位置(如锁竞争、IO 操作)。
    • 追踪系统关键日志(如冻结、Binder 通信、SurfaceFlinger 异常)。
  2. 治理策略

    • 应用层面:避免主线程耗时操作、优化锁策略、使用JobScheduler替代长耗时广播。
    • 系统层面:优化 Buffer 队列管理、监控 Binder 线程池状态、调整内存回收策略。

四、参考资料与工具

  • 案例参考:高爷 ANR 优化实践系列文章。
  • 分析工具adb bugreport、Systrace、Perfetto。
  • 关键日志am_anrdvm_lock_samplebinder_sampleSurfaceFlinger异常日志。