[TOC]
## ANR流程
**當應用發生ANR之后,系統會收集許多進程,來dump堆棧,從而生成ANR Trace文件,收集的第一個,也是一定會被收集到的進程,就是發生ANR的進程,接著系統開始向這些應用進程發送SIGQUIT信號,應用進程收到SIGQUIT后開始dump堆棧。**來簡單畫個示意圖:

所以,事實上進程發生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 性能優化
- 數據跨平臺