做者 | 藍灰_q數據庫
地址 | http://www.jianshu.com/p/54f945a15acb安全
聲明 | 本文是 藍灰_q 原創,已獲受權發佈,未經原做者容許請勿轉載網絡
前言關於ANR,之前只知道Activity、BroadCastReceiver、Service三種組件的ANR時限、通常採用哪些方式避免ANR、以及經過data/anr/traces.txt去分析ANR緣由,感受好像這就夠用了。app
可是,前幾天看源碼的時候,腦海裏忽然跳出一個問題:ide
ANR是怎麼判斷的5秒仍是10秒?函數
這個問題迅速擴大爲一連串問題:spa
ANR的時間定義在哪些文件裏?線程
ANR是怎麼計時的?日誌
是誰在監控ANR?server
全部的ANR都會彈窗嗎?
traces.txt能記錄多少內容?
ANR必定是app代碼問題致使的嗎?
前ANR機制的基本原理任何功能都有個執行者,以及業務邏輯的實現原理,在ANR上,就是兩個問題:
1.ANR是誰作的
ANR不多是在應用層作的,有兩個理由:
從能力上,發生ANR時,App本身已經死掉了,沒有能力處理;
從安全上,App的安全事實上是不可控的,若是有惡意App故意不處理ANR,設備就廢了。
因此,ANR的監控和處理,只能是在系統層作的。
系統層要作ANR的話,咱們首先想到是在AMS,由於AMS管理着全部的組件,也只有AMS知道組件是何時啓動的,以便判斷是否觸發了ANR。
不過,實際上,還有Activity Service,Input Manager Service這些服務,也參與了ANR的管理。
2.ANR是怎麼判斷超時時間的
這個問題,其實就是怎樣定義超時時間、何時開始計時、以及如何計時的問題。
ANR超時時間的定義,是在執行者,也就是系統服務那裏定義的,這個容易理解。
ANR從何時開始計時呢,在Android中,其實是系統服務在控制每一個組件的生命週期回調,因此能夠在這個邏輯入口開始計時。
至於如何計時的問題,其實Android系統裏已經有一個時間相對準確的機制,就是Handler機制,能夠用Handler機制發送延時消息,若是超時了,就發出ANR,若是沒有超時,就取消隊列裏的延時消息,這就解決了計時的問題。
這樣看來,ANR的基本原理以下:
不過,這個機制並不徹底,在判斷Activity的InputDispatching超時的狀況下,是InputDispatcher發現上一個事件沒有處理完,新的事件又進來了,纔會去走ANR。
各組件觸發ANR的過程會產生ANR的,包括Activity、Service、BroadCastReceiver、ContentProvider和Application,他們各自的ANR過程都有些不一樣,咱們先從簡單的看起。
1.BroadCastReceiver
時間定義
廣播的超時時間是定義在AMS裏:
BROADCAST_FG_TIMEOUT:10s
BROADCAST_BG_TIMEOUT:60s
前/後臺廣播是在發送Intent時,在intent.addFlag裏定義的。
觸發時機
當AMS處理廣播時,會調用processNextBroadcast函數,這裏面會處理並行廣播和串行廣播,其中,並行廣播是單向通知,不須要等待反饋,因此並行廣播沒有ANR。
在處理串行廣播時:
首先,判斷是否已經有一個廣播超時消息;而後,根據目標進程優先級,分別在前臺隊列和後臺隊列(超時時限不一樣)中排隊處理;接下來,根據不一樣的隊列,發出不一樣延時的ANR消息;若是處理及時,取消延時消息;若是處理超時,觸發ANR;
ANR處理
廣播的ANR處理相對簡單,主要是再次判斷是否超時、記錄日誌,記錄ANR次數等。
而後就繼續調用processNextBroadcast函數,處理下一條廣播了。
2.Service
Service真正的管理者是ActiveServices,AMS雖然會去交互與通訊,但在啓動服務時,是交給ActiveServices去作的。
時間定義
服務的超時時間是定義在AS裏:
SERVICE_TIMEOUT:20s;
SERVICE_BACKGROUND_TIMEOUT:200s;
在ActiveService執行startServiceLocked啓動服務時,會判斷啓動服務的發起方的進程(Process.THREAD_GROUP_BG_NONINTERACTIVE),以便選擇不一樣的超時時間。
觸發時機
ActivityServices會調用realStartServiceLocked函數啓動Service,最前面會先發送一個延遲消息,sendMessageAtTime(msg,time);其中的time,是在最開始startServiceLocked函數中判斷出前/後臺進程,而後裝在ServiceRecord中,一路傳過來的。
若是Service操做執行完畢,會執行serviceDoneExecutingLocked,這裏面會移除延遲消息。
若是Service執行超時,會執行mServices.serviceTimeout。
ANR處理
其實Service的ANR處理也相對簡單,記錄日誌,清理anr活動等。
mServices.serviceTimeout((Proce***ecord)msg.obj)函數裏,提供了進程信息。
3.ContentProvider
ContentProvider也是會ANR的,若是AMS中的ContentProviderClient在處理中超時,也能夠啓動ANR,超時時間和是否使用,由開發者決定:
CONTENT_PROVIDER_PUBLISH_TIMEOUT:10s
CONTENT_PROVIDER_RETAIN_TIME:20s
4.Application
Application的啓動是執行在主線程的,attachBaseContext和onCreate等回調也是在主線程的,這裏若是出現ANR,會影響到當前組件的運行。
5.Activity
Activity的ANR是相對最複雜的,也只有Activity中出現的ANR會彈出ANR提示框。
InputDispatching
Activity最主要的功能之一是交互,爲了方便交互,Android中的InputDispatcher會發出操做事件,最終在Input Manager Service中發出事件,經過InputChannel,向Activity分發事件。
交互事件必須獲得響應,若是不能及時處理,IMS就會報出ANR,交給AMS去彈出ANR提示框。
KeyDispatching
若是輸入是個Key事件,會從IMS進入ActivityRecord.Token.keyDispatchingTimeOut,而後進入AMS處理,不一樣的是,在ActivityRecord中,會先截留一次Key的不響應,只有當Key連續第二次處理超時,纔會彈出ANR提示框。
窗口焦點
Activity老是須要有一個當前窗口來響應事件的,但若是遲遲沒有當前窗口(得到焦點),好比在Activity切換時,舊Activity已經onPause,新的Activity一直沒有onResume,持續超過5秒,就會ANR。
App的生命週期太慢,或CPU資源不足,或WMS異常,均可能致使窗口焦點。
時間定義
在AMS中定義
KEY_DISPATCHING_TIMEOUT:5s
INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT :60s
觸發時機
輸入界面的觸發時機,毫不是儘快提示ANR,而是儘可能不提示ANR,由於ANR一旦提示出來,App通常也就關掉了。
對於InputDispatcher來講,若是有新的輸入事件時,上一個輸入事件尚未處理完,纔會通知IMS去判斷,是否須要處理ANR。
ANR處理
首先,寫日誌(data/anr/traces.txt)。
而後,會發出一個Message,彈出ANR提示框。
ANR的處理和日誌ANR是在AMS的appNotResponding函數中處理的,主要是記錄日誌,和彈出提示。
如何記錄
log日誌記錄在data/anr/traces.txt文件中,這個文件每次只記錄最近的一次ANR,有可能記錄失敗。
文件內容包括dump棧,CPU負載,IO Wait等
如何解讀
分析ANR,除了檢查代碼的生命週期函數是否有耗時操做,還能夠分析traces日誌,分析角度主要包括:
棧信息,通常能夠知道在哪段代碼附近發生了ANR,可能不是直接緣由,但通常在問題點附近。
CPU用量,看負載比例和平均負載,判斷是否是有別的App佔用了過多的CPU。
IO Wait,看IOWait的佔比是否很高,判斷是否在等待IO。
若是從根源上劃分的話,致使ANR的緣由有以下幾點:
IO操做,如數據庫、文件、網絡
CPU不足,通常是別的App佔用了大量的CPU,致使App沒法及時處理
硬件操做,如camera
線程問題,如主線程被join/sleep,或wait鎖等致使超時
service問題,如service忙致使超時無響應,或service binder的數量達到上限
system server問題,如WatchDog發現ANR