前言:本文所寫的是博主的個人見解,如有錯誤或者不恰當之處,歡迎私信博主,加以改正!原文連結,demo連結
合理編寫在世界各地獲得效能測試的程式碼,但仍然覺得緩慢,掛起或凍結很長時間,或者花費太長的時間來處理輸入。應用程式響應速度最糟糕的是“應用程式無響應”(ANR)對話方塊。
在Android中,系統會通過顯示一個對話方塊來防止在一段時間內響應不足的應用程式,該對話方塊顯示您的應用程式已停止響應,例如圖1中的對話方塊。
在這一點上,您的應用程式在相當長的一段時間內沒有反應,因此係統為使用者提供退出應用程式的選項。設計應用程式的響應性至關重要,因此係統不會向使用者顯示ANR對話方塊。
本文件描述了Android系統如何確定應用程式是否響應,並提供了確保應用程式保持響應的準則
是什麼原因引發的ANR?
通常,如果應用程式無法響應使用者輸入,系統將顯示ANR。例如,如果應用程式上的某些I / O操作(通常是網路訪問)阻塞UI執行緒,則系統無法處理傳入的使用者輸入事件。或者應用程式花費太多時間來構建一個精心設計的記憶體結構或者計算UI執行緒中游戲中的下一步。確保這些計算效率非常重要,但即使最有效的程式碼仍然需要執行時間。
在任何情況下,你的應用程式執行一個潛在的耗時的操作,你不應該在UI執行緒上的執行工作,而是建立一個工作執行緒,並在那裡進行大部分工作。這將保持UI執行緒(驅動使用者介面事件迴圈)的執行,並防止系統斷定您的程式碼已凍結。因為這樣的執行緒通常是在類級別上完成的,所以你可以將響應能力看成類的問題。(與基本程式碼效能進行比較,這是一個方法級別的關注。)
在 Android 中,應用程式響應由 ActivityManager
和 WindowManager
系統服務進行監控。當檢測到以下情況之一時,Android 將顯示特定應用程式的 ANR 對話方塊:
- 在5秒內沒有響應輸入事件(如按鍵或螢幕觸控事件)。
BroadcastReceiver
在10秒內尚未完成
如何避免ANR
預設情況下,Android應用程式通常在單執行緒上執行,“UI執行緒”或“主執行緒”。 這意味著您的應用程式在UI執行緒中執行的任務需要很長時間才能觸發ANR對話方塊,因為您的應用程式並沒有給自己一個處理輸入事件或意圖廣播的機會。
因此,在UI執行緒中執行的任何方法都應該在該執行緒上儘可能少的工作。 特別地,Activvity 應儘可能少地在關鍵的生命週期方法中設定,如 onCreate()
和 onResume()
。潛在的長時間執行操作(如網路或資料庫操作)或計算上昂貴的計算(如調整點陣圖大小)應在工作執行緒中完成(或者在資料庫操作的情況下,通過非同步請求)。
使用 AsyncTask 類建立一個更長時間操作的工作執行緒的最有效的方法。 只需擴充套件 AsyncTask 並實現 doInBackground()
方法來執行工作。 要向使用者釋出進度更改,可以呼叫 publishProgress()
,該呼叫會呼叫 onProgressUpdate()
回撥方法。 從實現 onProgressUpdate()
(在UI執行緒上執行),您可以通知使用者。 例如:
public class DownloadAsyncTask extends AsyncTask<URL,Integer,Long> {
//在這裡做長時間的工作
@Override
protected Long doInBackground(URL... params) {
return null;
}
//進度更新,每次呼叫publishProgress()時都會呼叫它
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
//當doInBackground()完成時呼叫
@Override
protected void onPostExecute(Long aLong) {
super.onPostExecute(aLong);
}
}複製程式碼
要執行此工作執行緒,只需建立一個例項並呼叫execute()
:
new DownloadAsyncTask().execute(url);複製程式碼
你可能想要建立自己的 Thread 或 HandleThread 類,雖然它比 AsyncTask 更復雜。如果你這樣做的話,應該呼叫Process.setThreadPriority()
並傳遞THREAD_PRIORITY_BACKGROUND
來設定執行緒的優先順序為“background”優先順序。如果沒有以這種方式去設定執行緒為較低的優先順序,那麼執行緒有可能會減慢你的應該程式,因為預設情況下它與 UI 執行緒的優先順序是相同的。
如果你實現了 Thread 或 HandleThread ,請確保你的 UI 執行緒在等待工作執行緒完成後不會被阻塞,請勿呼叫 Thread.wait()
或 Thread.sleep()
。在等待工作執行緒完成後,主執行緒不應該阻塞,而需要為其他執行緒提供一個處理程式,以便在完成後將其返回。以這種方式設計應用程式將允許你的應用程式的 UI 執行緒保持對輸入的響應,從而避免由5秒輸入事件超時引起的 ANR 對話方塊。
BroadcastReceiver 執行時間的具體約束強調了廣播接收者要做的事情:在後臺工作量小,如儲存設定或註冊通知。因此,與 UI 執行緒中呼叫其他方法一樣,應用程式應避免在廣播接收器中潛在的長時間執行的操作或者計算,但是如果需要做耗時操作來響應意向廣播,而不是通過工作執行緒進行密集的任務,則應該啟動 IntentService。
BroadcastReceiver物件的另一個常見問題是當它們執行得太頻繁時發生。頻繁的後臺執行減少了其他應用程式可用的記憶體量。
提示:你可以使用 StrictMode 來幫助找到潛在的長時間執行的操作,如網路或資料庫操作,您可能會意外地在主執行緒上執行。
增強應用響應
通常,100到200ms是使用者在應用程式中感覺的閾值,超過這個數值,使用者會覺得應用程式感知遲鈍。 因此,這裡有一些額外的技巧,你應該做什麼來避免ANR並使應用程式看起來響應使用者:
- 如果你的應用程式在後臺執行響應使用者輸入的工作,則顯示正在進行進度(例如在 UI 中使用 ProgressBar)。
- 對於遊戲來說,具體計算在工作執行緒中的進度。
- 如果您的應用程式具有耗時的初始設定階段,請考慮顯示啟動螢幕或儘快呈現主檢視,指示正在進行載入並非同步填充資訊。在這兩種情況下,您應該以某種方式指出進展情況,以免使用者察覺到應用程式被凍結。
- 使用 Systrace 和 Traceview 等效能工具來確定應用程式響應速度的瓶頸。