为了保证我们的应用程序保持响应,一个好的实践就是将执行缓慢的、计算耗时的操作从应用程序的主线程移进一个子线程中。
注意 :所有的Android应用程序——包括Activity、Service和Broadcast Receivers——在应用程序主线程中启动。因此,任何组件中耗时的操作将会阻塞包括Service和不可见的Activity等其他组件。
对于后台进程,Android提供了两种选择方案。先说下AsyncTask类,通过AsyncTask可以定义一个在后台执行的操作,然后提供一个事件处理程序,这样就可以用这个程序监控进程并且传递结果到GUI线程中。
或者,你也可以实现自己的线程,使用处理程序类在更新界面UI前和GUI线程做同步。这两种技术我们都会在下文提到。
使用后台线程对于避免对话框的“强制关闭”非常重要。Android中,Activity在5秒中内对于输入事件(如键盘按下)没有响应,Broadcast Receivers在10秒钟没有完成onReceive的处理,都被认为是没有回复的。
你不仅仅只想避免这种情况的发生,更不想对话框关闭。那就是用后台线程处理这些耗时操作吧,包括文件操作、网络查找、数据库事务和复杂计算。
使用AsyncTask运行异步任务
AsyncTask类提供了一种既简单又方便的途径就可以将你的耗时操作移到后台线程进行。与GUI线程异步同步的处理程序可以让你方便的在任务完成后更新Views控件和其他的UI元素的结果。
AsyncTask处理着所有线程的创建、管理和异步刷新,使你能够创建一个异步同步任务在操作完成后维护后台操作和UI更新的处理的一致性。
异步同步任务的创建
你需要从AsyncTask继承来创建你的异步同步任务,如代码清单1所示。你的实现应该指明方法execute的入参类型,后台计算执行过程的进度类型, 后台线程执行返回结果类型,具体参数格式如下:
AsyncTask<[Input Parameter Type], [Progress Report Type], [Result Type]>
如果你不需要输入参数、更新进度或者返回值,只需简单的指定相应的类型为Void。
代码清单1:
private class MyAsyncTask extends AsyncTask {
@Override
protected void onProgressUpdate(Integer... progress) {
// [... Update progress bar, Notification, or other UI element ...]
}
@Override
protected void onPostExecute(Integer... result) {
// [... Report results via UI update, Dialog, or notification ...]
}
@Override
protected Integer doInBackground(String... parameter) {
int myProgress = 0;
// [... Perform background processing task, update myProgress ...]
PublishProgress(myProgress)
// [... Continue performing background processing task ...]
// Return the value to be passed to onPostExecute
return result;
}
}
代码清单1说明你的子类应该实现以下几个事件处理函数:
1.doInBackground
需要在你的实现类里定义了类型的一组参数,这个方法会在后台线程中执行,所以不能在UI里面调用。
耗时代码通常写在这儿,使用publishProgress方法通过onProgressUpdate将进度更新到UI中。当你的后台任务完成后,返回值将被传递到onPostExecute处理程序中反馈给UI。
2.onProgressUpdate
重写此方法传递过程中的更新到UI线程中。该方法接收一组由doInBackground传递来的参数将其传入publishProgress。此方法被执行的时候和GUI线程做同步,所以你可以安全的修改你的UI元素。
3.onPostExecute
当doInBackground执行完以后,返回值将被传入这个事件处理函数中。
一旦你的异步刷新任务完成,就可使用此方法去更新UI。此方法是同步刷版伴随着GUI线程的执行,因此你可以安全的修改UI元素。
执行异步同步任务
一旦你实现了你的异步同步任务,通过创建一个实例和调用execute就可以执行你的任务,如代码清单2所示。你可以传入一些参数,这些参数的类型在你的实现里面已经定义过了。
代码清单2:
new MyAsyncTask().execute("inputString1", "inputString2");
注意:每个AsyncTask仅可以被执行一次,如果你尝试再一次调用execute将会抛出一个异常。