1. 程式人生 > >Android中什麼是ANR?怎麼解決ANR?

Android中什麼是ANR?怎麼解決ANR?

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

在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或者快速顯示主畫面並非同步來填充這些資訊。在這兩種情況下,你都應該顯示正在進行的進度,以免使用者認為應用程式被凍結了。