深刻理解ANR

深刻理解ANR

 

做者 | 藍灰_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的緣由

若是從根源上劃分的話,致使ANR的緣由有以下幾點:

  • IO操做,如數據庫、文件、網絡

  • CPU不足,通常是別的App佔用了大量的CPU,致使App沒法及時處理

  • 硬件操做,如camera

  • 線程問題,如主線程被join/sleep,或wait鎖等致使超時

  • service問題,如service忙致使超時無響應,或service binder的數量達到上限

  • system server問題,如WatchDog發現ANR

相關文章
相關標籤/搜索