🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[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)