[TOC]
## ANR流程
**当应用发生ANR之后,系统会收集许多进程,来dump堆栈,从而生成ANR Trace文件,收集的第一个,也是一定会被收集到的进程,就是发生ANR的进程,接着系统开始向这些应用进程发送SIGQUIT信号,应用进程收到SIGQUIT后开始dump堆栈。**来简单画个示意图:
![](https://img.kancloud.cn/72/89/7289593eeb2fa625801369ede133a821_1080x570.png)
所以,事实上进程发生ANR的整个流程,也只有dump堆栈的行为会在发生ANR的进程中执行。这个过程从收到SIGQUIT开始(圈1),到使用socket写Trace(圈2)结束,然后再继续回到server进程完成剩余的ANR流程。我们就在这两个边界上做做文章。
首先我们肯定会想到,我们能否监听到syste\_server发送给我们的SIGQUIT信号呢?如果可以,我们就成功了一半。
## 监控SIGQUIT信号
使用*sigaction*方法注册signal handler进行异步监听。
### Signal Handler
那我们再试下另一种方法是否可行,我们通过可以*sigaction*方法,建立一个Signal Handler:
~~~
void signalHandler(int sig, siginfo_t* info, void* uc) {
~~~
建立了Signal Handler之后,我们发现在同时有*sigwait*和signal handler的情况下,信号没有走到我们的signal handler而是依然被系统的Signal Catcher线程捕获到了,这是什么原因呢?
原来是Android默认把SIGQUIT设置成了BLOCKED,所以只会响应*sigwait*而不会进入到我们设置的handler方法中。我们通过*pthread\_sigmask*或者*sigprocmask*把SIGQUIT设置为UNBLOCK,那么再次收到SIGQUIT时,就一定会进入到我们的handler方法中。需要这样设置:
~~~
sigset_t sigSet;
~~~
最后需要注意,我们通过Signal Handler抢到了SIGQUIT后,原本的Signal Catcher线程中的*sigwait*就不再能收到SIGQUIT了,原本的dump堆栈的逻辑就无法完成了,**我们为了ANR的整个逻辑和流程跟原来完全一致,需要在Signal Handler里面重新向Signal Catcher线程发送一个SIGQUIT**:
~~~
int tid = getSignalCatcherThreadId(); //遍历/proc/[pid]目录,找到SignalCatcher线程的tid
~~~
*(如果缺少了重新向SignalCatcher发送SIGQUIT的步骤,AMS就一直等不到ANR进程写堆栈,直到20秒超时后,才会被迫中断,而继续之后的流程。直接的表现就是ANR弹窗非常慢(20秒超时时间),并且/data/anr目录下无法正常生成完整的 ANR Trace文件。)*
以上就得到了一个不改变系统行为的前提下,比较完善的监控SIGQUIT信号的机制,这也是我们监控ANR的基础。
## 误报
**充分非必要条件1:发生ANR的进程一定会收到SIGQUIT信号;但是收到SIGQUIT信号的进程并不一定发生了ANR。**
考虑下面两种情况:
* **其他进程的ANR**:上面提到过,发生ANR之后,发生ANR的进程并不是唯一需要dump堆栈的进程,系统会收集许多其他的进程进行dump,也就是说当一个应用发生ANR的时候,其他的应用也有可能收到SIGQUIT信号。**进一步,我们监控到SIGQUIT时,可能是监听到了其他进程产生的ANR****,从而产生误报。**
* **非ANR发送SIGQUIT**:发送SIGQUIT信号其实是很容易的一件事情,开发者和厂商都可以很容易的发送一个SIGQUIT(java层调用*android.os.Process.sendSignal*方法;Native层调用*kill或者tgkill*方法),**所以我们可能会收到非ANR流程发送的SIGQUIT信号,从而产生误报。**
### 处理
在ANR弹窗前,会执行到*makeAppNotRespondingLocked*方法中,在这里会给发生ANR进程标记一个*NOT\_RESPONDING*的flag。而这个flag我们可以通过ActivityManager来获取:
监控到SIGQUIT后,我们在20秒内(20秒是ANR dump的timeout时间)不断轮询自己是否有NOT\_RESPONDING对flag,一旦发现有这个flag,那么马上就可以认定发生了一次ANR。
[微信Android客户端的ANR监控方案](https://mp.weixin.qq.com/s/fWoXprt2TFL1tTapt7esYg)
- Android
- 四大组件
- Activity
- Fragment
- Service
- 序列化
- Handler
- Hander介绍
- MessageQueue详细
- 启动流程
- 系统启动流程
- 应用启动流程
- Activity启动流程
- View
- view绘制
- view事件传递
- choreographer
- LayoutInflater
- UI渲染概念
- Binder
- Binder原理
- Binder最大数据
- Binder小结
- Android组件
- ListView原理
- RecyclerView原理
- SharePreferences
- AsyncTask
- Sqlite
- SQLCipher加密
- 迁移与修复
- Sqlite内核
- Sqlite优化v2
- sqlite索引
- sqlite之wal
- sqlite之锁机制
- 网络
- 基础
- TCP
- HTTP
- HTTP1.1
- HTTP2.0
- HTTPS
- HTTP3.0
- HTTP进化图
- HTTP小结
- 实践
- 网络优化
- Json
- ProtoBuffer
- 断点续传
- 性能
- 卡顿
- 卡顿监控
- ANR
- ANR监控
- 内存
- 内存问题与优化
- 图片内存优化
- 线下内存监控
- 线上内存监控
- 启动优化
- 死锁监控
- 崩溃监控
- 包体积优化
- UI渲染优化
- UI常规优化
- I/O监控
- 电量监控
- 第三方框架
- 网络框架
- Volley
- Okhttp
- 网络框架n问
- OkHttp原理N问
- 设计模式
- EventBus
- Rxjava
- 图片
- ImageWoker
- Gilde的优化
- APT
- 依赖注入
- APT
- ARouter
- ButterKnife
- MMKV
- Jetpack
- 协程
- MVI
- Startup
- DataBinder
- 黑科技
- hook
- 运行期Java-hook技术
- 编译期hook
- ASM
- Transform增量编译
- 运行期Native-hook技术
- 热修复
- 插件化
- AAB
- Shadow
- 虚拟机
- 其他
- UI自动化
- JavaParser
- Android Line
- 编译
- 疑难杂症
- Android11滑动异常
- 方案
- 工业化
- 模块化
- 隐私合规
- 动态化
- 项目管理
- 业务启动优化
- 业务架构设计
- 性能优化case
- 性能优化-排查思路
- 性能优化-现有方案
- 登录
- 搜索
- C++
- NDK入门
- 跨平台
- H5
- Flutter
- Flutter 性能优化
- 数据跨平台