ANR产生原理

ANR(Application Not Responding)是 Android 对 “应用主线程卡死”系统级保护机制
输入事件、广播、服务 等在规定时间内未被处理完毕,SystemServer 会弹框并杀进程,防止整个系统跟着假死。

计时起点:事件离开 SystemServer 的瞬间

// InputDispatcher.cpp
void InputDispatcher::startDispatchCycleLocked(...) {// 主线程 5s 超时(默认)timeout = AMS::getInputDispatchingTimeout(timeout);mLooper->sendMessageDelayed(timeout, ...);
}
  • 按键/触摸:5s

  • 前台广播:10s

  • 后台广播:60s

  • 前台服务:20s

  • 后台服务:200s

计时终点:App 主线程处理完事件并回执

App 主线程在 ViewRootImpl$WindowInputEventReceiver 里消费完事件后,会调用:

// ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {public void onInputEvent(InputEvent event) {... // 派发、measure、layout、drawfinishInputEvent(event, true); // ← 通知系统“我干完了”}
}

通过 Binder IPC 回到 InputDispatcher,取消第 1 步的超时任务。

超时未回执 → InputDispatcher 直接发SIGQUIT给 App

若第 2 步没在规定时间内到达,native 层会:

// InputDispatcher.cpp
void InputDispatcher::onDispatchTimeoutLocked(...) {pid_t pid = connection->getPid();kill(pid, SIGQUIT);          // ① 发送 SIGQUIT(3)// 同时把超时信息写入 dropbox
}
  • SIGQUIT 默认行为是终止进程(可被信号处理器捕获)。

  • 系统利用这个信号触发 Dalvik/ART dump trace,生成 /data/anr/traces.txt

SystemServer 收到“超时报告”→ 弹 ANR 对话框 → 杀进程

// ActivityManagerService.java
void inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {synchronized (this) {ProcessRecord proc = findProcessLocked(pid);if (proc == null) return;// ④ 弹出系统对话框mUiHandler.post(() -> showAnrDialog(proc, reason));// ⑤ 10s 后若用户未选择“等待”则杀进程mHandler.sendMessageDelayed(MSG_KILL_ANR, proc, 10_000);}
}
  • 用户点 “关闭应用” → 立即 killProcessQuiet(pid)

  • 用户点 “等待” → 取消杀进程,但计时器重新启动(再卡一次就再来 5s)

场景超时信号最终动作
主线程死循环5sSIGQUIT弹框 + 杀进程
onCreate() 超 10s10s广播超时同上
后台服务 200s200s服务超时同上

一句话背板

事件离开系统时开始倒计时,主线程处理完回执取消;超时未回 → InputDispatcher 发 SIGQUIT,AMS 弹框并 10s 后杀进程;主线程别做长活,用子线程/Handler 异步。

ANR Log分析-binder

ANR 日志

----- pid 1887 at 2010-06-23 00:20:50.043623522+0800 -----
Cmd line: com.android.systemui
Build fingerprint: 'chery/cheryidcpro/cheryidcpro:14/UQ1A.240205.002/eng.autoli.20250416.022302:userdebug/dev-keys'
ABI: 'arm64'
Build type: optimized
suspend all histogram:	Sum: 41.458ms 99% C.I. 1us-1736.960us Avg: 73.768us Max: 5367us
DALVIK THREADS (133):
"main" prio=5 tid=1 Runnable| group="main" sCount=0 ucsCount=0 flags=0 obj=0x72c5fd28 self=0xb400007c496897b0| sysTid=1887 nice=-10 cgrp=top-app sched=0/0 handle=0x7e022f84f8| state=R schedstat=( 723564350007 288749055735 4147215 ) utm=44375 stm=27981 core=3 HZ=100| stack=0x7fd2ed3000-0x7fd2ed5000 stackSize=8188KB| held mutexes= "mutator lock"(shared held)native: #00 pc 0055be4c  /apex/com.android.art/lib64/libart.so (art::DumpNativeStack+172) (BuildId: 55b9536b61b755b7bae82e95aa498dab)native: #01 pc 006773a8  /apex/com.android.art/lib64/libart.so (art::Thread::DumpStack const+340) (BuildId: 55b9536b61b755b7bae82e95aa498dab)native: #02 pc 00694f38  /apex/com.android.art/lib64/libart.so (art::DumpCheckpoint::Run+916) (BuildId: 55b9536b61b755b7bae82e95aa498dab)native: #03 pc 006782d4  /apex/com.android.art/lib64/libart.so (art::Thread::RunCheckpointFunction+184) (BuildId: 55b9536b61b755b7bae82e95aa498dab)native: #04 pc 00729b28  /apex/com.android.art/lib64/libart.so (artJniMethodStart+88) (BuildId: 55b9536b61b755b7bae82e95aa498dab)native: #05 pc 0020f8f8  /apex/com.android.art/lib64/libart.so (art_jni_method_start+40) (BuildId: 55b9536b61b755b7bae82e95aa498dab)at android.os.Parcel.nativeFreeBuffer(Native method)at android.os.Parcel.freeBuffer(Parcel.java:5249)at android.os.Parcel.recycle(Parcel.java:577)at android.bluetooth.IBluetooth$Stub$Proxy.getRemoteName(IBluetooth.java:2624)at android.bluetooth.BluetoothDevice.getName(BluetoothDevice.java:1751)at android.bluetooth.BluetoothDevice.getAlias(BluetoothDevice.java:1818)at com.android.settingslib.bluetooth.CachedBluetoothDevice.getName(CachedBluetoothDevice.java:599)at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0(LoadedApk.java:1811)at android.app.LoadedApk$ReceiverDispatcher$Args.$r8$lambda$gDuJqgxY6Zb-ifyeubKeivTLAwk(unavailable:0)at android.app.LoadedApk$ReceiverDispatcher$Args$$ExternalSyntheticLambda0.run(unavailable:2)at android.os.Handler.handleCallback(Handler.java:958)at android.os.Handler.dispatchMessage(Handler.java:99)at android.os.Looper.loopOnce(Looper.java:205)at android.os.Looper.loop(Looper.java:294)at android.app.ActivityThread.main(ActivityThread.java:8248)at java.lang.reflect.Method.invoke(Native method)at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:981)

上面这段 ANR trace 堪称「教科书级」的 主线程-Binder 被拖死 现场,把整条证据链完整地摆在了我们面前

进程快照

----- pid 1887 at 2010-06-23 00:20:50+0800 -----
Cmd line: com.android.systemui
  • 发生 ANR 的正是 SystemUI(状态栏、导航栏、蓝牙弹窗都由它负责)。

  • 时间戳虽然是 2010,这是编译时写死的 mock 时间,不影响分析。

主线程状态快照

"main" prio=5 tid=1 Runnable
state=R schedstat=( 723564350007 288749055735 4147215 )
held mutexes= "mutator lock"(shared held)
  • state=R 表示 正在 CPU 上跑,不是传统「阻塞」。

  • utm+stm=72 s 已吃掉 72 s CPU 时间,说明 死循环 / 长时间原地打转

  • 还抱着 mutator lock,GC 想停都停不下来,整个进程世界都被它拖住

调用栈快照(关键帧)

at android.os.Parcel.nativeFreeBuffer(Native method)
at android.os.Parcel.recycle(Parcel.java:577)
at android.bluetooth.IBluetooth$Stub$Proxy.getRemoteName(IBluetooth.java:2624)
at android.bluetooth.BluetoothDevice.getName(BluetoothDevice.java:1751)
at android.bluetooth.BluetoothDevice.getAlias(BluetoothDevice.java:1818)
at com.android.settingslib.bluetooth.CachedBluetoothDevice.getName(CachedBluetoothDevice.java:599)
at com.autolink.systemui.plugin.status.view.BlueToothPopView.getData(BlueToothPopView.java:316)
  • 最顶层 正在 JNI 释放 Parcel 内存 (nativeFreeBuffer)。

  • 该 Parcel 是 刚走完 Binder 调用 getRemoteName() 的返回包。

  • 意味着:
    BluetoothDevice.getName() → Binder → bluetooth@service → 等待回复 → 一直等到现在

  • 主线程 同步等结果,结果 remote 端不返回,于是 卡在释放返回包 这一步。

业务入口快照

  • 触发点在 蓝牙状态广播 (BluetoothAdapter.ACTION_STATE_CHANGEDACL_CONNECTED 之类)。

  • 收到广播后 直接在主线程 遍历已配对设备并 依次 getName()/getAlias()

  • 如果 远端设备无响应(耳机/车机断电、固件 bug、驱动卡死),Binder 就会阻塞,主线程同步被拖住。

ANR Log分析-input

Subject: Input dispatching timed out (9b06661 NavigationBar0 (server) is not responding. Waited 5001ms for MotionEvent(deviceId=-1, eventTime=105593089000000, source=TOUCHSCREEN, displayId=0, action=DOWN, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=1.0, yPrecision=1.0, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1748.0, 1152.0)]), policyFlags=0x6b000000).
RssHwmKb: 578736
RssKb: 442240
RssAnonKb: 248084
RssShmemKb: 9312
VmSwapKb: 102988--- CriticalEventLog ---
capacity: 20
timestamp_ms: 1746257995372
window_ms: 300000----- dumping pid: 17251 at 105598116----- pid 17251 at 2025-05-03 15:39:55.140104249+0800 -----
Cmd line: com.android.systemui
Build fingerprint: 'chery/cheryidcpro/cheryidcpro:14/UQ1A.240205.002/eng.autoli.20250430.025459:userdebug/dev-keys'
ABI: 'arm64'
Build type: optimized
suspend all histogram:	Sum: 92.523ms 99% C.I. 2.660us-10708.479us Avg: 249.388us Max: 22989us
DALVIK THREADS (134):
"main" prio=5 tid=1 Runnable| group="main" sCount=0 ucsCount=0 flags=0 obj=0x722c2f08 self=0xb400006ecd557380| sysTid=17251 nice=0 cgrp=foreground sched=0/0 handle=0x70cfae14f8| state=R schedstat=( 473063282543 2713319119711 2741705 ) utm=33543 stm=13763 core=0 HZ=100| stack=0x7fc4435000-0x7fc4437000 stackSize=8188KB| held mutexes= "mutator lock"(shared held)native: #00 pc 0055be4c  /apex/com.android.art/lib64/libart.so (art::DumpNativeStack+172) (BuildId: 55b9536b61b755b7bae82e95aa498dab)native: #01 pc 006773a8  /apex/com.android.art/lib64/libart.so (art::Thread::DumpStack const+340) (BuildId: 55b9536b61b755b7bae82e95aa498dab)native: #02 pc 00694f38  /apex/com.android.art/lib64/libart.so (art::DumpCheckpoint::Run+916) (BuildId: 55b9536b61b755b7bae82e95aa498dab)native: #03 pc 006782d4  /apex/com.android.art/lib64/libart.so (art::Thread::RunCheckpointFunction+184) (BuildId: 55b9536b61b755b7bae82e95aa498dab)native: #04 pc 0072a710  /apex/com.android.art/lib64/libart.so (artTestSuspendFromCode+52) (BuildId: 55b9536b61b755b7bae82e95aa498dab)native: #05 pc 00226e1c  /apex/com.android.art/lib64/libart.so (art_quick_test_suspend+156) (BuildId: 55b9536b61b755b7bae82e95aa498dab)at java.lang.reflect.Executable.getMethodNameInternal(Native method)at java.lang.reflect.Method.getName(Method.java:115)at java.lang.reflect.Method$1.compare(Method.java:72)at java.lang.reflect.Method$1.compare(Method.java:67)at java.util.TimSort.gallopRight(TimSort.java:617)at java.util.TimSort.mergeHi(TimSort.java:863)at java.util.TimSort.mergeAt(TimSort.java:520)at java.util.TimSort.mergeForceCollapse(TimSort.java:461)at java.util.TimSort.sort(TimSort.java:254)at java.util.Arrays.sort(Arrays.java:1344)at java.util.ArrayList.sort(ArrayList.java:1738)at java.util.Collections.sort(Collections.java:210)at libcore.util.CollectionUtils.removeDuplicates(CollectionUtils.java:86)at java.lang.Class.getMethods(Class.java:2226)at android.os.Handler.handleCallback(Handler.java:958)at android.os.Handler.dispatchMessage(Handler.java:99)at android.os.Looper.loopOnce(Looper.java:205)at android.os.Looper.loop(Looper.java:294)at android.app.ActivityThread.main(ActivityThread.java:8248)at java.lang.reflect.Method.invoke(Native method)at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:981)

这段日志是 SystemUI 进程(com.android.systemui) 被系统判定为 输入ANR(Input Dispatching Timed Out) 的完整现场。

核心结论一句话:

主线程并没有死锁、也没有 Binder 阻塞,而是陷在「Class.getMethods() → 反射排序」里连续耗 CPU,导致 InputDispatcher 的 MotionEvent 5 秒没人消费,被系统报 ANR。

关键信息速览

项目日志原文解读
ANR 类型Input dispatching timed out ... NavigationBar0 ... Waited 5001ms输入事件(手指按下)在 5 s 内未被 SystemUI 主线程消费
进程 & 线程pid=17251 tid=1 main就是 SystemUI 的主线程
线程状态state=R schedstat=(473063282543 ...)一直处于 RunnableCPU 在跑(不是 Block)
内存RssKb: 442240 VmSwapKb: 102988物理内存 442 MB,Swap 占用 100 MB轻度内存压力,但 不是 GC 停滞
栈顶TimSort.gallopRight/mergeHi → Arrays.sort → Class.getMethods正在对 Method[] 做排序去重反射耗时

为什么会卡在这里

调用链

主线程消息 → Handler.handleCallback → ...
→ ReflectUtils.findMethod() → Class.getMethods()
→ CollectionUtils.removeDuplicates() → Collections.sort() → Arrays.sort()
→ TimSort.mergeHi/gallopRight

Class.getMethods() 会把 本类 + 所有父类的所有 public 方法 一次性取出,数量可达数百个
然后 CollectionUtils.removeDuplicates()方法名 + 参数类型 排序去重,最坏情况下 TimSort 比较器要执行上万次反射字符串比较

主线程跑满 CPU

schedstatutm=33543 stm=13763(单位 10 ms),
仅本次采样就已经 累计 335 s 用户态 + 137 s 内核态
说明主线程 一直在跑,但 就是没干完,InputDispatcher 的事件队列因此得不到空档期处理。

不是 GC、也不是 Binder

  • 没有 SuspendAll 几百毫秒的记录(GC 阻塞典型特征)。

  • 栈里无 IPCThreadState::waitForResponse 等 Binder 阻塞符号。

  • 内存 Swap 虽有 100 MB,但 anon 部分只有 248 MB,远未到频繁 swap-in/out 的程度。

根因修复动作
主线程做重型反射 + 排序① 把 Class.getMethods() 与排序 移到工作线程
② 结果 (),皮肤切换只读缓存;
③ 若框架不可改,:Application 起就异步解析完常用 View 的方法;
缓存 key 建议(类, 方法名, 参数类型数组) 做 key,避免每次拼接字符串比较;
避免反射长期用 接口/策略模式 替代「运行时找方法」;

一句话总结

这不是死锁,也不是内存抖动,而是 SystemUI 主线程在「给反射方法排序」时 CPU 被吃光,输入事件 5 秒得不到处理,被系统判为输入 ANR;把反射+排序移出主线程并加缓存即可根治

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/98601.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/98601.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/98601.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

stm32——单总线,DHT11

目录 一、单总线协议的原理和应用 单总线协议指的是只采用一根信道来进行数据传输,通信指的是双方(MCU与传感器)通过一根信道进行数据交互,所以按照数据的传输方向,只能采用半双工通信方式,比较典型的传感器…

css3之grid布局

容器:gird container开启grid布局的元素 项目:grid items容器里面的子元素,不包括后代元素 显式网格(单元格):通过grid-template-columns和grid-template-rows指定的网格,注意项目不等于单元格,…

C++容器:list

一、list的介绍及使用 list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素…

STL库——map/set(类函数学习)

ʕ • ᴥ • ʔ づ♡ど 🎉 欢迎点赞支持🎉 个人主页:励志不掉头发的内向程序员; 专栏主页:C语言; 文章目录 前言 一、序列式容器和关联式容器 二、set 系列的使用 2.1、set 和 multiset 参考文档 2.2、set…

计算机网络IP协议

1.TCP协议1.1 确认应答1.2 超时重传1.3 连接管理1.4 滑动窗口1.5 流量控制1.6 拥塞控制 1.7 延时应答1.8 稍带应答1.9 粘包问题1.10 异常情况2.IP协议 网络层2.1 NAT机制下的几种情况:同一个局域网中,内网ip访问 内网 ip,可以的不同局域网中,内网IP访问 内网IP,不行~~外网IP访…

Windows电脑如何查看wifi连接记录及连接时间

查询WIFI 连接的记录 echo netsh wlan show profiles netsh wlan show wlanreport POWERSHELL 脚本 Get-WinEvent -LogName Microsoft-Windows-WLAN-AutoConfig/Operational | Where-Object { $_.Id -in (8001,8002) } | Select-Object TimeCreated, Id, {Name"Action…

【golang学习笔记 gin 】1.2 redis 的使用

安装redis go get -u github.com/gin-gonic/gin go get -u github.com/go-redis/redis/v8创建相关目录 gotest->conifg->database.go->redis.go->controller ->index.go->model->user.go->router->router.gomain.go 封装Redis package config impor…

Java学习之——“IO流“的进阶流之序列化流的学习

一、核心概念:什么是序列化与反序列化?序列化 (Serialization): 将一个对象(在内存中的状态)转换成一个字节序列的过程。这个字节序列包含了对象的数据、对象的类型以及对象中存储的属性等信息。反序列化 (Deserializa…

机器学习04——决策树(信息增益、信息增益率、ID3、C4.5、CART、剪枝、连续值缺失值处理)

上一章:机器学习03——线性模型 下一章:机器学习05——多分类学习与类别不平衡 机器学习实战项目:【从 0 到 1 落地】机器学习实操项目目录:覆盖入门到进阶,大学生就业 / 竞赛必备 文章目录一、决策树的基本流程&#…

(论文速读)从语言模型到通用智能体

论文题目:From Multimodal LLMs to Generalist Embodied Agents: Methods and Lessons(从多模式大型语言模型到多面手具身代理:方法和教训)会议:CVPR2025摘要:我们研究了多模态大型语言模型(Multimodal Large Language…

【Epiq Solutions】Matchstiq™ G20 和 Matchstiq™ G40 AI SDR

Matchstiq™ G20 和 Matchstiq™ G40 产品简介 Matchstiq™ G20 和 Matchstiq™ G40 是 Epiq Solutions 推出的 紧凑型、高性能软件定义无线电(SDR)平台,专为满足 严苛 SWaP-C(体积、重量、功耗受限)场景下的战术与移动…

基于Echarts+HTML5可视化数据大屏展示-旅游智慧中心

效果展示&#xff1a; 代码结构&#xff1a;主要代码实现 index.html布局 <!DOCTYPE html> <html lang"en" style"font-size: 97.5px;"> <head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"…

Docker 镜像的使用

1.镜像的基本信息[roothost1 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu latest 802541663949 2 weeks ago 78.1MB hello-world latest 1b44b5a3e06a 4 weeks ago 10.1kB执行 docker images 命令时加上 --no…

网络编程;套接字;TCP通讯;UDP通讯;0909

思维导图TCP服务器端和客户端通讯服务器端 代码#include<myhead.h> #define SER_IP "192.168.109.12"//我的虚拟机的ip #define SER_PORT 8888 int main() {//1.创建一个用于连接的套接字文件描述符int sfd socket(AF_INET,SOCK_STREAM,0);if(sfd-1){perror(&…

贪心算法应用:柔性制造系统(FMS)刀具分配问题详解

Java中的贪心算法应用&#xff1a;柔性制造系统(FMS)刀具分配问题详解 1. 问题背景与定义 柔性制造系统(Flexible Manufacturing System, FMS)是现代智能制造中的关键组成部分&#xff0c;它能够灵活地适应不同产品的生产需求。在FMS中&#xff0c;刀具分配是一个核心优化问题&…

不止是DELETE:MySQL多表关联删除的JOIN语法实战详解

MySQL 的 ​​DELETE​​ 语句用于从数据库表中删除记录。这是一项非常强大且危险的操作&#xff0c;因为一旦执行&#xff0c;数据通常无法恢复。理解其语法和安全实践至关重要。以下是 MySQL 删除语句的详细指南。一、 核心语法&#xff1a;DELETE​​DELETE​​ 语句用于删除…

ubuntu 系統使用過程中黑屏問題分析

背景&#xff1a; 工欲善其事&#xff0c;必先利其器。作为程序员&#xff0c;想要得到更好的发展&#xff0c;遇到问题直接baidu, google 虽然可以得到一些参考或者答案&#xff0c;但是也会降低自己的思考能力&#xff0c;本文以ubuntu 使用过程中黑屏这一问题为背景&#x…

Redis(45)哨兵模式与集群模式有何区别?

Redis 提供了两种高可用性解决方案&#xff1a;哨兵模式和集群模式。它们各自有不同的特点和适用场景。以下是详细的对比和结合代码的示例&#xff1a; 哨兵模式&#xff08;Sentinel&#xff09; 特点高可用性&#xff1a; Sentinel 通过监控、通知、故障转移等功能&#xff0…

微信小程序如何进行分包处理?

目录 分包是什么&#xff1f; 为什么要分包&#xff1f; 分包前后结构对比 具体操作步骤 第 1 步&#xff1a;规划分包结构 第 2 步&#xff1a;修改 app.json 进行配置 第 3 步&#xff1a;创建分包目录并移动文件 第 4 步&#xff1a;处理组件和工具函数的引用 第 5…

Go语言极速入门与精要指南从零到精通的系统化学习路径

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 持续学习&#xff0c;不断…