andorid ANR keyDispatchingTimedOut的原因和解決之道

Android最佳實踐之響應靈敏性

可能會存在這樣的情況,你寫的代碼通過了世界上所有的性能測試,但當用戶嘗試使用你的應用程序時,仍然讓用戶感到不爽。應用程序響應不夠靈敏的地方包括——反映遲鈍,掛起或凍結很長時間,或者需要花費很長的時間來處理輸入。

 

在Android上,如果你的應用程序有一段時間響應不夠靈敏,系統會向用戶顯示一個對話框,這個對話框稱作應用程序無響應(ANR:Application Not Responding)對話框。用戶可以選擇讓程序繼續運行,但是,他們在使用你的應用程序時,並不希望每次都要處理這個對話框。因此,在程序裏對響應性能的設計很重要,這樣,系統不會顯示ANR給用戶。

 

一般說來,如果應用程序不能響應用戶輸入的話,系統會顯示一個ANR。例如,一個應用程序阻塞在一些I/O操作上(通常是網絡訪問),這時,應用程序的主線程就不能再處理用戶的輸入事件。經過一定的時間後,系統認爲應用程序已經掛起,並顯示ANR來讓用戶選擇殺死應用程序。

 

相似地,如果你的應用程序花費太多的時間來構建詳細的內存結構,或者也許是在遊戲裏花費太多時間來計算下一步移動,這時,系統會認爲你的應用程序已經掛起。因此,確保這些計算是高效的往往很重要,但即使是最高效的代碼仍然需要花費時間來運行。

 

在這兩種情況下,解決的方法通常是創建一個子線程,然後在線程裏做你的大部分工作。這能讓主線程(驅動UI事件循環)保持運行,並阻止系統認爲你的代碼已經凍結。因爲這些線程通常是在類級別上完成的,因此,你可以認爲響應性能問題是一個類的問題。(與基本性能相比而言,基本性能問題認爲是方法級別的問題)

 

這篇文章將討論Android系統如何判斷一個應用程序處於無響應狀態,併爲保證應用程序的響應性提供嚮導。

 

這篇文章囊括這些主題:

  • 什麼引發了ANR?
  • 如何避免ANR?
  • 增強響應靈敏性

 

1)什麼引發了ANR?

在Android裏,應用程序的響應性是由Activity Manager和Window Manager系統服務監視的。當它監測到以下情況中的一個時,Android就會針對特定的應用程序顯示ANR:

  • 在5秒內沒有響應輸入的事件(例如,按鍵按下,屏幕觸摸)
  • BroadcastReceiver在10秒內沒有執行完畢

clip_image002

一個ANR對話框顯示給用戶

2)如何避免ANR?

 

考慮上面的ANR定義,讓我們來研究一下爲什麼它會在Android應用程序裏發生和如何最佳構建應用程序來避免ANR。

 

Android應用程序通常是運行在一個單獨的線程(例如,main)裏。這意味着你的應用程序所做的事情如果在主線程裏佔用了太長的時間的話,就會引發ANR對話框,因爲你的應用程序並沒有給自己機會來處理輸入事件或者Intent廣播。

 

因此,運行在主線程裏的任何方法都儘可能少做事情。特別是,Activity應該在它的關鍵生命週期方法(如onCreate()和onResume())裏儘可能少的去做創建操作。潛在的耗時操作,例如網絡或數據庫操作,或者高耗時的計算如改變位圖尺寸,應該在子線程裏(或者以數據庫操作爲例,通過異步請求的方式)來完成。然而,不是說你的主線程阻塞在那裏等待子線程的完成——也不是調用Thread.wait()或是Thread.sleep()。替代的方法是,主線程應該爲子線程提供一個Handler,以便完成時能夠提交給主線程。以這種方式設計你的應用程序,將能保證你的主線程保持對輸入的響應性並能避免由於5秒輸入事件的超時引發的ANR對話框。這種做法應該在其它顯示UI的線程裏效仿,因爲它們都受相同的超時影響。

 

IntentReceiver執行時間的特殊限制意味着它應該做:在後臺裏做小的、瑣碎的工作如保存設定或者註冊一個Notification。和在主線程裏調用的其它方法一樣,應用程序應該避免在BroadcastReceiver裏做耗時的操作或計算。但不再是在子線程裏做這些任務(因爲BroadcastReceiver的生命週期短),替代的是,如果響應Intent廣播需要執行一個耗時的動作的話,應用程序應該啓動一個Service。順便提及一句,你也應該避免在Intent Receiver裏啓動一個Activity,因爲它會創建一個新的畫面,並從當前用戶正在運行的程序上搶奪焦點。如果你的應用程序在響應Intent廣播時需要向用戶展示什麼,你應該使用Notification Manager來實現。

 

3)增強響應靈敏性

 

一般來說,在應用程序裏,100到200ms是用戶能感知阻滯的時間閾值。因此,這裏有一些額外的技巧來避免ANR,並有助於讓你的應用程序看起來有響應性。

 

  • 如果你的應用程序爲響應用戶輸入正在後臺工作的話,可以顯示工作的進度(ProgressBar和ProgressDialog對這種情況來說很有用)。
  • 特別是遊戲,在子線程裏做移動的計算。
  • 如果你的應用程序有一個耗時的初始化過程的話,考慮可以顯示一個Splash Screen或者快速顯示主畫面並異步來填充這些信息。在這兩種情況下,你都應該顯示正在進行的進度,以免用戶認爲應用程序被凍結了。

######################################

Android ANR分析

15:59:37 I/ActivityManager(130): ANR in process: com.android.email (last in com.android.email)

=>frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
=>提示輸出cpu信息
Annotation: keyDispatchingTimedOut
CPU usage:

=>frameworks/base/services/java/com/android/server/ProcessStats.java
=>輸出cpu當前狀態
=>/proc/loadavg 顯示cpu負荷
=>1-分鐘平均負載 / 5-分鐘平均負載 / 15-分鐘平均負載
Load: 4.37 / 4.55 / 3.97
=>cpu狀態的時間段
CPU usage from 10987ms to 27ms ago:
  =>/proc/state讀取cpu的使用情況
  =>http://linux.die.net/man/5/proc
  =>user
  =>kernel
  =>iowait
  =>irq -> 0
  =>softirq -> 0
  =>minor
  The number of minor faults the process has made which have not required loading a memory page from disk. 
  =>major
  The number of major faults the process has made which have required loading a memory page from disk.
  system_server: 12% = 4% user + 7% kernel / faults: 1886 minor
  m.android.email: 12% = 6% user + 5% kernel / faults: 2716 minor
  sensorserver_ya: 7% = 0% user + 7% kernel
  breeze.launcher: 3% = 0% user + 3% kernel / faults: 94 minor
  ocess.msn.shell: 0% = 0% user + 0% kernel / faults: 38 minor
  m.android.phone: 0% = 0% user + 0% kernel
  alog: 0% = 0% user + 0% kernel
  rpcrotuer_smd_x: 0% = 0% user + 0% kernel
  rild: 0% = 0% user + 0% kernel
  alog: 0% = 0% user + 0% kernel
  events/0: 0% = 0% user + 0% kernel
  port-bridge: 0% = 0% user + 0% kernel
TOTAL: 81% = 13% user + 25% kernel + 42% iowait

15:59:37 I/ActivityManager(130): Removing old ANR trace file from /data/anr/traces.txt
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章