Android 系統穩定性 - ANR(一)


       
 如果你是一個Android應用程序開發人員,你的人生中不可避免的三件事情是:死亡、繳稅和ANR。這麼說是誇張了,但是由於Android本身的設計,以及應用程序和系統在開發過程中的缺陷,經常會在測試過程中遇到各種各樣的ANR問題。在功能性的測試中還少一些,主要是在壓力測試中(例如Monkey測試)會遇到非常多的ANR問題。本章的目的就是彙總筆者在工作中遇到的各種ANR問題,將其歸納總結出一套分析和處理ANR問題的方法,希望能夠通過這套方法爲大家提供思路,有效的減少大家處理ANR問題的時間。同時也會給出一些避免ANR的最佳實踐,更多的從預防做起,更少的做事後補救。

考慮到看本文的讀者大多是有實際經驗的開發人員,我會盡量少的提到一些基礎的概念,我也希望給大家更多的“乾貨”。

 

  本章的主要內容如下:

  1. ANR簡介(什麼是ANR、爲什麼會有ANRANR的異常長什麼樣)

  2. 如何分析ANR(引起ANR的原因分類、分析ANR的利器)

  3. 實例講解

  4. 避免ANR的最佳實踐(從錯誤中吸取教訓)

 1.1ANR簡介

        ANR,是“Application Not Responding”的縮寫,即“應用程序無響應”。在Android中,ActivityManagerService(簡稱AMS)和WindowManagerService(簡稱WMS)會監測應用程序的響應時間,如果應用程序主線程(即UI線程)在超時時間內對輸入事件沒有處理完畢,或者對特定操作沒有執行完畢,就會出現ANR。對於輸入事件沒有處理完畢產生的ANRAndroid會顯示一個對話框,提示用戶當前應用程序沒有響應,用戶可以選擇繼續等待或者關閉這個應用程序(也就是殺掉這個應用程序的進程)。



1.1.1 爲什麼會有ANR

        如上所述,ANR的產生需要同時滿足三個條件:

  • 主線程:只有應用程序進程的主線程響應超時纔會產生ANR

  • 超時時間:產生ANR的上下文不同,超時時間也會不同,但只要在這個時間上限內沒有響應就會ANR

  • 輸入事件/特定操作:輸入事件是指按鍵、觸屏等設備輸入事件,特定操作是指BroadcastReceiverService的生命週期中的各個函數,產生ANR的上下文不同,導致ANR的原因也會不同;

        針對這三個條件,有以下三種情況會觸發ANR,詳細說明如下。

  1. 主線程對輸入事件在5秒內沒有處理完畢

        Android的事件系統從2.3開始做了完全不同的實現,原先2.2中是在Java層實現的,但在2.3中整體轉移到了C++層,本書基於2.3以後的版本進行說明。我們先簡單瞭解一下產生這種ANR的整個流程<!-- 在這章之前要先講Android的事件機制?還是放在後一章講?偏向於放在後面。 -->

當應用程序的Window處於Active狀態並且能夠接收輸入事件(例如按鍵事件、觸摸事件等)時,系統底層上報的事件就會被InputDispatcher分發給這個應用程序,應用程序的主線程通過InputChannel讀取輸入事件並交給界面視圖處理,界面視圖是一個樹狀結構,DecorView是視圖樹的根,事件從樹根開始一層一層向端點(例如一個Button)傳遞。我們通常會註冊一個監聽器來接收並處理事件,或者創建自定義的視圖控件來處理事件。

        InputDispatcher運行在系統進程(進程名爲system_server)的一個單獨的線程中,應用程序的主線程在處理事件的過程中,InputDispatcher會不斷的檢測處理過程是否超時,一旦超時,會通過一系列的回調通知WMSnotifyANR函數,最終會調用到AMSmHandler對象裏的SHOW_NOT_RESPONDING_MSG這個case,此時界面上就顯示系統提示對話框了,同時使用logcat命令查看log(日誌信息)也可以看到關於ANR的信息。InputDispatcher就是那個愛打“小報告”的傢伙。

 

        一下子出現了好幾個重要的名詞,要深入瞭解這種情況的ANR,需要熟悉Android的事件機制,本書會在別的章節中詳細分析,這裏只需要記住他們的功能:

  • Window:具體指的是PhoneWindow對象,表示一個能夠顯示的窗口,它能夠接收系統分發的各種輸入事件;

  • InputDispatcher:將系統上報的輸入事件分發給當前活動的窗口;

  • InputChannelInputDispatcher和應用程序分別運行在兩個不同的進程中,InputDispatcher就是通過InputChannel將事件對象傳遞給應用進程的。



注意:產生這種ANR的前提是要有輸入事件,如果用戶沒有觸發任何輸入事件,即便是主線程阻塞了,也不會產生ANR,因爲InputDispatcher沒有分發事件給應用程序,當然也不會檢測處理超時和報告ANR了。



  1. 主線程在執行BroadcastReceiveronReceive函數時10秒內沒有執行完畢

        BroadcastReceiver(簡稱BR)的onReceive函數運行在主線程中,當這個函數超過10秒鐘沒有返回就會觸發ANR。不過對這種情況的ANR系統不會顯示對話框提示,僅是輸出log而已。



  1. 主線程在執行Service的各個生命週期函數時20秒內沒有執行完畢

        Service的各個生命週期函數也運行在主線程中,當這些函數超過20秒鐘沒有返回就會觸發ANR。同樣對這種情況的ANR系統也不會顯示對話框提示,僅是輸出log

 

三種ANR中只有第1種會顯示系統提示對話框,因爲用戶正在做界面交互操作,如果長時間沒有任何響應,會讓用戶懷疑設備死機了,大多數人此時會開始亂按,甚至拔出電池重啓,給用戶的體驗肯定是非常糟糕的。

        三種ANR發生時都會在log中輸出錯誤信息,你會發現各個應用進程和系統進程的函數堆棧信息都輸出到了一個/data/anr/traces.txt的文件中,這個文件是分析ANR原因的關鍵文件,同時在日誌中還會看到當時的CPU使用率,這也是重要信息,在後面的章節會詳細介紹如何利用它們分析ANR問題。

這三種ANR不是孤立的,有可能會相互影響。例如一個應用程序進程中同時有一個正在顯示的Activity和一個正在處理消息的BroadcastReceiver,它們都運行在這個進程的主線程中。如果BRonReceive函數沒有返回,此時用戶點擊屏幕,而onReceive超過5秒仍然沒有返回,主線程無法處理用戶輸入事件,就會引起第1ANR。如果繼續超過10秒沒有返回,又會引起第2ANR

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章