使用后台线程最简便的方式是使用AsyncTask工具类,可覆盖其中的doInBackground(...)方法,如下:
private class FetchItemsTask extends Astends AsyncTask<Void,Void,Void>{
@Override
protected Void doInBackground(Void... parms){
...;
return null;
}
}
启动AsyncTask时只需要调用execute()方法。
在后台线程成功获取到数据后,要想更新UI,我们第一想法是在doInBackground(...)方法的尾部调用更新UI的方法,但是这种方法很容易导致内存对象间步调不一致的冲突从而导致应用的崩溃。因此,为避免安全隐患,不推荐也不允许在后台线程中更新UI。
而AsyncTask提供另一个可覆盖的onPostExecute(...)方法。该方法在doInBackground(...)方法执行完毕后才会允许,而且它是在主线程而非后台线程上允许。因此,在该方法中更新UI比较安全。
二、AsyncTask的类型参数
下面说说AsyncTask的类型参数。
第一个类型参数可指定输入参数的类型。如下:
AsyncTask<String,Void,Void> task = new AsyncTask<String,Void,Void>(){
public Void doInBackground(String... params){
for (String parameter : params) {
Log.i(TAG , "Received parameter: " + parameter);
}
return null;
}
};
task.execute("First parameter" , "Second parameter" , "Etc.");
输入参数传入execute(...)方法(可接受一个或多个参数)。然后,这些变量参数再传递给doInBackground(...)方法。
第二个类型参数可指定发送进度更新需要的类型。如下:
final ProgressBar progressBar = /* A determinate progress bar */;
progressBar.setMax(100);
AsyncTask<Integer,Integer,Void> task = new AsyncTask<Integer,Integer,Void>(){
public Void doInBackground(Integer... params){
for (Integer progress : params){
publishProgress(progress);
Thread.sleep(1000);
}
}
public void onProgressUpdate(Integet... params){
int progress = params[0];
progressBar.setProgress(progress);
}
};
task.execute(25,50,75,100);
进度更新通常发生在执行的后台进程中。在后台进程中,我们无法完成必要的UI更新。因此AsyncTask提供了publishProgress(...)和onProgressUpdate(...)两个方法,其工作方式如下:
在后台线程中,我们从doInBackground(...)方法中调用publishProgress(...)方法。这样onProgressUpdate(...)方法便能够在UI线程上被调用。因此我们可在onProgressUpdate(...)方法中执行UI更新,但我们必须在doInBackground(...)方法中使用publishProgress(...)方法对它们进行管理。
第三个类型参数是AsyncTask返回结果的数据类型。它设置了doInBackground(...)方法返回结果的类型以及onPostExecute(...)方法输入参数的数据类型。
三、AsyncTask的清理
在一些复杂的使用场景下,我们需将AsyncTask赋值给实例变量。一旦能够掌控它,我们就可以随时调用AsyncTask.cancel(boolean)方法,撤销运行中的AsyncTask。AsyncTask.cancel(boolean)方法有两种工作模式:粗暴的和温和的。如调用温和的candel(false)方法,该方法会设置isCancelled()的状态为true。随后AsyncTask会检查doInBackground(...)方法中的isCancelled()状态,然后选择提前结束运行。然而,如调用粗暴的candel(true)方法,它会直接终止doInBackground(...)方法当前所在的线程。