標籤:
文章都為原創,轉載請註明出處,未經允許而盜用者追究法律責任。
很久之前寫的了,留著有點浪費,共用之。
編寫者:李文棟
http://rayleeya.iteye.com/blog/1955652
如果你是一個Android應用程式開發人員,你的人生中不可避免的三件事情是:死亡、繳稅和ANR。這麼說是誇張了,但是由於Android本身的設計,以及應用程式和系統在開發過程中的缺陷,經常會在測試過程中遇到各種各樣的ANR問題。在功能性的測試中還少一些,主要是在壓力測試中(例如Monkey測試)會遇到非常多的ANR問題。本章的目的就是匯總筆者在工作中遇到的各種ANR問題,將其歸納總結出一套分析和處理ANR問題的方法,希望能夠通過這套方法為大家提供思路,有效減少大家處理ANR問題的時間。同時也會給出一些避免ANR的最佳實務,更多的從預防做起,更少的做事後補救。
考慮到看本文的讀者大多是有實際經驗的開發人員,我會盡量少的提到一些基礎的概念,我也希望給大家更多的“乾貨”。
本章的主要內容如下:
ANR簡介(什麼是ANR、為什麼會有ANR、ANR的異常長什麼樣)
如何分析ANR(引起ANR的原因分類、分析ANR的利器)
執行個體講解
避免ANR的最佳實務(從錯誤中吸取教訓)
1.1 ANR簡介
ANR,是“Application Not Responding”的縮寫,即“應用程式無響應”。在Android中,ActivityManagerService(簡稱AMS)和WindowManagerService(簡稱WMS)會監測應用程式的回應時間,如果應用程式主線程(即UI線程)在逾時時間內對輸入事件沒有處理完畢,或者對特定操作沒有執行完畢,就會出現ANR。對於輸入事件沒有處理完畢產生的ANR,Android會顯示一個對話方塊,提示使用者當前應用程式沒有響應,使用者可以選擇繼續等待或者關閉這個應用程式(也就是殺掉這個應用程式的進程)。
1.1.1 為什麼會有ANR
如上所述,ANR的產生需要同時滿足三個條件:
主線程:只有應用程式進程的主線程響應逾時才會產生ANR;
逾時時間:產生ANR的上下文不同,逾時時間也會不同,但只要在這個時間上限內沒有響應就會ANR;
輸入事件/特定操作:輸入事件是指按鍵、觸屏等裝置輸入事件,特定操作是指BroadcastReceiver和Service的生命週期中的各個函數,產生ANR的上下文不同,導致ANR的原因也會不同;
針對這三個條件,有以下三種情況會觸發ANR,詳細說明如下。
主線程對輸入事件在5秒內沒有處理完畢
Android的事件系統從2.3開始做了完全不同的實現,原先2.2中是在Java層實現的,但在2.3中整體轉移到了C++層,本書基於2.3以後的版本進行說明。我們先簡單瞭解一下產生這種ANR的整個流程<!-- 在這章之前要先講Android的事件機制?還是放在後一章講?偏向於放在後面。 -->。
當應用程式的Window處於Active狀態並且能夠接收輸入事件(例如按鍵事件、觸摸事件等)時,系統底層上報的事件就會被InputDispatcher分發給這個應用程式,應用程式的主線程通過InputChannel讀取輸入事件並交給介面視圖處理,介面視圖是一個樹狀結構,DecorView是視圖樹的根,事件從樹根開始一層一層向端點(例如一個Button)傳遞。我們通常會註冊一個監聽器來接收並處理事件,或者建立自訂的視圖控制項來處理事件。
InputDispatcher運行在系統進程(進程名為system_server)的一個單獨的線程中,應用程式的主線程在處理事件的過程中,InputDispatcher會不斷的檢測處理過程是否逾時,一旦逾時,會通過一系列的回調通知WMS的notifyANR函數,最終會調用到AMS中mHandler對象裡的SHOW_NOT_RESPONDING_MSG這個case,此時介面上就顯示系統提示對話方塊了,同時使用logcat命令查看log(日誌資訊)也可以看到關於ANR的資訊。InputDispatcher就是那個愛打“小報告”的傢伙。
一下子出現了好幾個重要的名詞,要深入瞭解這種情況的ANR,需要熟悉Android的事件機制,本書會在別的章節中詳細分析,這裡只需要記住他們的功能:
Window:具體指的是PhoneWindow對象,表示一個能夠顯示的視窗,它能夠接收系統分發的各種輸入事件;
InputDispatcher:將系統上報的輸入事件分發給當前活動的視窗;
InputChannel:InputDispatcher和應用程式分別運行在兩個不同的進程中,InputDispatcher就是通過InputChannel將事件對象傳遞給應用進程的。
注意:產生這種ANR的前提是要有輸入事件,如果使用者沒有觸發任何輸入事件,即便是主線程阻塞了,也不會產生ANR,因為InputDispatcher沒有分發事件給應用程式,當然也不會檢測處理逾時和報告ANR了。
主線程在執行BroadcastReceiver的onReceive函數時10秒內沒有執行完畢
BroadcastReceiver(簡稱BR)的onReceive函數運行在主線程中,當這個函數超過10秒鐘沒有返回就會觸發ANR。不過對這種情況的ANR系統不會顯示對話方塊提示,僅是輸出log而已。
主線程在執行Service的各個生命週期函數時20秒內沒有執行完畢
Service的各個生命週期函數也運行在主線程中,當這些函數超過20秒鐘沒有返回就會觸發ANR。同樣對這種情況的ANR系統也不會顯示對話方塊提示,僅是輸出log。
三種ANR中只有第1種會顯示系統提示對話方塊,因為使用者正在做介面互動操作,如果長時間沒有任何響應,會讓使用者懷疑裝置死機了,大多數人此時會開始亂按,甚至拔出電池重啟,給使用者的體驗肯定是非常糟糕的。
三種ANR發生時都會在log中輸出錯誤資訊,你會發現各個應用進程和系統進程的函數堆棧資訊都輸出到了一個/data/anr/traces.txt的檔案中,這個檔案是分析ANR原因的關鍵檔案,同時在日誌中還會看到當時的CPU使用率,這也是重要訊息,在後面的章節會詳細介紹如何利用它們分析ANR問題。
這三種ANR不是孤立的,有可能會相互影響。例如一個應用程式進程中同時有一個正在顯示的Activity和一個正在處理訊息的BroadcastReceiver,它們都運行在這個進程的主線程中。如果BR的onReceive函數沒有返回,此時使用者點擊螢幕,而onReceive超過5秒仍然沒有返回,主線程無法處理使用者輸入事件,就會引起第1種ANR。如果繼續超過10秒沒有返回,又會引起第2種ANR。
[轉寄]Android 系統穩定性 - ANR(一)