资讯详情

Android ANR 全解析 与 traces 分析实战

📅 2026/10/24 7:38:42 | 华诺云谱 👁 阅读
Android ANR 全解析 与 traces 分析实战
Android ANR 全解析与 traces 分析实战技术复盘 · 面试复习手册四类超时判定 · 系统级 Watchdog · 三段式栈分析目录一、先厘清概念ANR 到底是谁判定的二、四类 ANR 与超时阈值三、还有一类系统级 ANRWatchdog四、按根因分类比背类型更有价值五、traces 文件怎么读三段式方法六、三个典型实战案例七、定位工具箱八、预防应用侧与系统侧九、面试速答卡摘要TL;DRANR 不是卡了 5 秒这么简单它是 AMS 对四类超时输入 5 秒、广播 10/60 秒、Service 20/200 秒、Provider 10 秒的判定结果而 traces 文件里的 main 线程栈顶、锁持有者和 CPU usage 段才是定位根因的三把钥匙。一、先厘清概念ANR 到底是谁判定的ANRApplication Not Responding的本质是系统对应用响应能力的一次超时判定判定方不是用户、也不是应用自己而是运行在system_server中的 AMS 以及 InputDispatcher。判定发生后系统会做三件事把应用所有线程的堆栈 dump 到/data/anr/目录Android 11 起文件名形如anr_2026-05-20-10-30-00早期统一写traces.txt在 logcat 中打印ANR in 包名与Reason:弹出应用无响应对话框部分场景如后台 Service、Provider 超时不弹框直接记日志。注意只有主线程被卡住才会 ANR。后台线程再慢也不会直接触发 ANR但它可能占满 CPU 或持有主线程需要的锁从而间接导致 ANR。二、四类 ANR 与超时阈值类型触发条件阈值日志特征判定位置输入事件分发超时InputDispatcher 已把按键/触摸事件发给窗口应用 5 秒内未消费5 sReason: Input dispatching timed outInputDispatcher 上报 AMS广播超时广播接收者的onReceive()未在时限内返回前台 10 s / 后台 60 sBroadcast of Intent { ... }BroadcastQueueService 超时onCreate/onStartCommand/onBind等生命周期回调未完成前台 20 s / 后台 200 sexecuting service 包名/服务名ActiveServicesContentProvider 超时Provider 进程拉起或 publishProvider 未完成10 sContentProvider not respondingProcessList进程启动超时记忆口诀输入 5、广播 10/60、服务 20/200、Provider 10。几个容易答错的细节服务端超时口径在 Android 8 之后固定为前台 20 秒、后台 200 秒不要再背老版本的后台 20 分钟广播的前台/后台取决于发送方是否处于前台不是接收方Android 12 起前台服务启动限制更严但 ANR 阈值未变。三、还有一类系统级 ANRWatchdog面试官如果问你处理过系统死机吗答的就是这一层。system_server内部有一个 Watchdog 线程默认 60 秒为一轮向若干被监控者AMS 的 main handler、PMS 的 handler、WMS 的全局锁等投放检测消息。若 60 秒内没有回执Watchdog 会dump 所有 Java 线程栈与 native 栈到/data/anr/打印*** WATCHDOG KILLING SYSTEM PROCESS ***主动杀死system_serverzygote 感知后重启框架部分平台直接整机重启。所以手机突然黑屏重启、看不到应用崩溃的现场第一件事就是找WATCHDOG KILLING这个关键字。四、按根因分类比背类型更有价值根因典型栈特征常见场景锁等待Blocked栈中有waiting to lock ... held by thread N主线程与子线程共用锁子线程做耗时 IO 时持锁主线程 IO栈顶在SharedPreferencesImpl、SQLiteConnection、FileInputStreamonCreate里读配置、写日志Binder 同步阻塞栈顶在BinderProxy.transactNative主线程调系统服务或其它进程的 AIDL 同步接口消息队列堆积栈顶在MessageQueue.next之下的业务代码且 utm 很高大量 Handler 消息串行执行输入事件排在队尾CPU 饥饿主线程栈正常甚至nativePollOnce但 CPU usage 段显示被占满后台线程死循环、其它进程抢占GC 抖动栈中Daemon/HeapTaskDaemon活跃utm 偏高内存抖动频繁触发 GC系统服务端阻塞主线程卡在ActivityManagerProxy/PackageManagerProxyPMS 安装扫描、WMS 布局耗时、system_server 锁竞争五、traces 文件怎么读三段式方法读 traces 的关键不是看得多而是看得准。下面按三段固定顺序拆解每一段只回答一个问题。一份 traces 文件包含多个进程的完整栈几万行起步。不要从头读按下面顺序看三段。5.1 第一段现象行与 Reason----- pid 4213 at 2026-05-20 10:30:12 ----- Cmd line: com.example.app Build fingerprint: xxx/release-keys ABI: arm64往上翻日志里的ANR in com.example.app重点看Reason:与PID:先把哪一类 ANR、卡在哪个进程确定下来。5.2 第二段main 线程栈核心中的核心main prio5 tid1 Blocked | groupmain sCount1 dsCount0 flags1 obj0x12c00000 self0x7f8a1c0000 | sysTid4213 nice-10 cgrpdefault sched0/0 handle0x7f9b2c4a50 | stateS schedstat( 1200000000 8500000000 4200 ) utm95 stm25 core3 HZ100 | stack0x7fe3a00000 stackSize8192KB | held mutexes at com.example.core.LockHelper.refresh(LockHelper.java:42) - waiting to lock 0x0a1b2c3d (a java.lang.Object) held by thread 27 at com.example.MainActivity.onResume(MainActivity.java:88) at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1456)这份栈要读出四个信息线程状态Blocked不是Running说明主线程在等锁waiting to lock ... held by thread 27锁被 27 号线程持有马上去找Thread-27的栈utm95 stm25主线程累计用户态 95 个时钟 tick说明它不是刚被唤醒而是已经干了不少活schedstat的第二项 8.5 秒可运行但没抢到 CPU 的时间这个值大就高度怀疑 CPU 饥饿。常见的良性栈形别误判at android.os.MessageQueue.nativePollOnce(Native method)且状态为Native/Waiting主线程空闲等待消息正常at java.lang.Object.wait且是Waiting等待通知需结合业务判断。5.3 第三段CPU usage 段CPU usage from 0ms to 5000ms later: 78% 4213/com.example.app: 70% user 8% kernel / faults: 1200 minor 52% 4213/com.example.app (main thread) 18% 4218/OkHttp Dispatcher 12% 812/media.codec: 12% user解读规则主线程自身 CPU 高主线程在死循环、密集计算或频繁 GC主线程 CPU 低但其它线程/进程高CPU 饥饿型 ANR主线程抢不到时间片总和接近 100%系统整体过载需要看是否有第三方进程或系统服务异常。5.4 补充Binder 阻塞怎么确认如果 main 栈顶是下面的样子说明主线程在等system_server或其它进程返回at android.os.BinderProxy.transactNative(Native method) at android.app.IActivityManager$Stub$Proxy.startService(...)此时要找 traces 中对应进程通常是system_server的栈看它卡在哪把锁上——这才是跨进程死锁的真实位置。六、三个典型实战案例下面三个现场覆盖了八成以上的 ANR 投诉锁、跨进程、CPU。每个案例只保留关键栈片段训练从栈到结论的直觉。案例 1主线程等锁最高频现象点击首页按钮后 5 秒弹 ANR。关键栈main prio5 tid1 Blocked at com.example.db.ConfigDao.get(ConfigDao.java:31) - waiting to lock 0x0a1b2c3d held by thread 27 Thread-27 prio5 tid27 Runnable at com.example.net.HttpClient.query(HttpClient.java:120) - locked 0x0a1b2c3d (a java.lang.Object)结论子线程 27 持锁做网络请求主线程被迫等待。修复锁粒度收窄把网络请求移出临界区主线程改为异步回调。案例 2Binder 同步调用引发的跨进程死锁现象开机向导进入设置页必现 ANR且system_server也出现卡顿。应用侧main ... Native at android.os.BinderProxy.transactNative(Native method) at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(...)system_server侧Binder:1234_5 ... Blocked at com.android.server.wm.ActivityTaskManagerService.startActivity(...) - waiting to lock 0x0b2c held by main main ... Blocked at com.android.server.wm.WindowManagerService.layoutWindow(...) - waiting to lock 0x0c3d held by Binder:1234_5结论应用主线程同步等 PMSPMS 又在等 WMS 的锁而 WMS 的锁被另一个正在等应用的 Binder 线程持有形成环。修复应用侧改异步查询系统侧减少持锁时间避免在锁内做跨进程调用。案例 3CPU 饥饿型最容易被误判为代码写得烂现象低端机上偶发 ANR栈看起来完全正常。关键特征main 栈顶为nativePollOnce空闲schedstat第二项达数秒CPU usage 段显示某后台进程占 60% 以上。结论不是应用代码卡住而是系统整体 CPU 被占满输入事件分发不出去。修复定位抢占进程多为异常后台服务、日志抓取、频繁 GC 的第三方 SDK必要时用 cgroup 限流或调低优先级。七、定位工具箱# 1. 抓取现网 ANR 文件需 root 或 userdebugadb shellls-l/data/anr/ adb pull /data/anr/anr_2026-05-20-10-30-00# 2. 事件日志中的 ANR 埋点adb logcat-bevents|grep-Eam_anr|am_proc_start# 3. 实时观察输入分发是否卡住adb shell dumpsys input|grep-A20FocusedWindow# 4. 查看当前进程与线程 CPU 占用adb shelltop-H-p进程号# 5. 系统服务锁竞争userdebugadb shell dumpsys activity processes adb shell dumpsys window离线分析顺序建议Reason→ main 栈 → 锁持有者 → 对端进程栈 → CPU usage。按这个顺序八成现场能在十分钟内定性。八、预防应用侧与系统侧ANR 治理要分两侧看应用侧决定会不会卡系统侧决定卡了之后能不能快速恢复。应用侧写代码时就能规避主线程不做 IO、不碰数据库、不读大文件onReceive()中超过 10 秒的工作改用goAsync()或转 Service/WorkManager避免主线程同步调用 Binder改用回调或线程池减少synchronized范围尤其不要持锁做网络/IO开启 StrictMode在开发期暴露主线程磁盘与网络访问Application 的onCreate只做初始化重活延迟到首屏后。系统侧ROM/平台工程师的职责缩短system_server关键锁的持有时间避免锁内跨进程调用监控开机后前 3 分钟的 CPU 峰值限制预装应用的并发启动对高频 ANR 的服务做专项治理如 PMS 扫描、WMS 布局在 userdebug 版本常开 Watchdog 日志量产版本保留触发时的 dump 能力。九、面试速答卡问ANR 有哪几种答表面四类超时——输入 5 秒、广播前台 10 秒后台 60 秒、Service 前台 20 秒后台 200 秒、Provider 10 秒本质都是主线程 5 秒内无法处理输入事件。再往上一层还有system_server的 Watchdog60 秒无响应会杀系统进程属于整机级的系统 ANR。问怎么定位 ANR答先看Reason定类型再看 traces 里 main 线程的栈顶与状态然后顺着held by thread N找到锁持有者必要时对比对端进程Binder 对端的栈最后用 CPU usage 段区分自己在忙还是CPU 被抢。四步能覆盖绝大多数现场。问你做过哪些 ANR 治理答分两层讲——应用层收敛主线程 IO 与锁粒度系统层治理system_server锁竞争与开机 CPU 峰值配合 traces 自动化解析脚本把高频 ANR 聚合成 TOP 榜按模块派单。元信息项目说明目标读者Android 系统开发工程师、Framework 层开发者、面试冲刺者前置知识Android 进程模型、Binder 机制、AMS 基本职责、Linux 线程状态技术栈版本Android 8.0 至 Android 14阈值与路径以 AOSP 主线为准
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑