在Android中,系统通过显示ANR警示框来保护程序的长时间无响应。对话框如下:
此时,你的应用已经经历过一段时间的无法响应了,因此系统提供用户可以退出应用的选择。为你的程序提供良好的响应性是至关重要的,这样才能够避免系统为用户显示ANR的警示框。
是什么导致了ANR?(What Triggers ANR?)
如果一个程序在UI线程执行I/O操作(通常是网络请求或者是文件读写),这样系统就无法处理用户的输入事件。
在Android中,程序的响应性是由Activity Manager与Window Manager系统服务来负责监控的。当系统监测到下面的条件之一时会显示ANR的对话框:
- 对输入事件(例如硬件点击或者屏幕触摸事件),5秒内都无响应。
- BroadReceiver不能够在10秒内结束接收到任务。
如何避免ANRs(How to Avoid ANRs)
任何执行在UI线程的方法都应该尽可能的简短快速。特别是,在activity的生命周期的关键方法onCreate()
与onResume()
方法中应该尽可能的做比较少的事情。类似网络或者DB操作等可能长时间执行的操作,或者是类似调整bitmap大小等需要长时间计算的操作,都应该执行在工作线程中。(在DB操作中,可以通过异步的网络请求)。
为了执行一个长时间的耗时操作而创建一个工作线程最方便高效的方式是使用AsyncTask
。只需要继承AsyncTask并实现doInBackground()
方法来执行任务即可。为了把任务执行的进度呈现给用户,你可以执行publishProgress()
方法,这个方法会触发onProgressUpdate()
的回调方法。在onProgressUpdate()
的回调方法中(它执行在UI线程),你可以执行通知用户进度的操作,
,例如:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
// Do the long-running work in here
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
// This is called each time you call publishProgress()
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
// This is called when doInBackground() is finished
protected void onPostExecute(Long result) {
showNotification("Downloaded " + result + " bytes");
}
}
为了能够执行这个工作线程,只需要创建一个实例并执行execute()
:
new DownloadFilesTask().execute(url1, url2, url3);
相比起AsycnTask来说,创建自己的线程或者HandlerThread稍微复杂一点。