线程通信之AsyncTask
Handler消息的处理机制,就是Android的异步消息处理机制的核心思想,一条消息通过这样一个流程,从子线程进入到主线程,从不能更新UI变成了可以更新UI。
为了更方便我们在子线程中对UI进行操作,除了Handler+Thread的方式,Android提供了另外一些好用的工具:AsyncTask、ThreadPoolExecutor、IntentService。本片内容的主角是AsyncTask。
public abstract class AsyncTask
自定义AsyncTask,及其方法介绍:
/**
* AsyncTask可以正确轻松地使用UI线程。
* 此类允许您执行后台操作并在UI线程上发布结果,而无需操作线程和/或handlers。
*
* abstract class AsyncTask<Params, Progress, Result>
* Params:执行AsyncTask时需要传入的参数,可用于后台任务中使用
* Progress:后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位
* Result:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型
*/
private class DownloadTask extends AsyncTask<URL, Integer, Boolean> {
/**
* 在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
}
/**
* onPreExecute()方法执行后立即在后台线程执行该方法
* 所有的耗时操作应该这这里执行,任务完成可以通过return语句返回执行后的结果,
* 也可以不返回(第三个泛型参数应为Void)
* <p>
* 注意:该方法中不可以进行UI操作
*
* @param params
* @return
*/
@Override
protected Boolean doInBackground(URL... params) {
/**
* 反馈当前任务的执行速度
* 可以从#doInBackground调用此方法,以便在后台计算仍在运行时在UI线程上发布更新。
* 每次调用此方法将触发在UI线程上执行#onProgressUpdate。
*/
publishProgress();
return false;
}
/**
* 在后台任务中调用了publishProgress()方法后,会很快调用该方法
* 方法中携带的参数就是后台任务中传递过来的,在该方法中可以对UI进行操作,利用参数值对UI进行相应更新
*
* @param values
*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
/**
* 后台任务(doInBackground)执行完毕并通过return语句进行返回时,调用该方法。
* 返回的数据作为参数传入该方法,利用返回的数据进行一些UI操作,比如提醒任务执行的结果,
* 以及关掉进度条对话框等
*
* @param b 在{@link #doInBackground(Object [])}中计算的结果(如果有的话),可以为null
*/
@Override
protected void onPostExecute(Boolean b) {
super.onPostExecute(b);
}
/**
* Runs on the UI thread after {@link #cancel(boolean)} is invoked and
* {@link #doInBackground(Object[])} has finished.
* <p>
* 默认实现简单地调用{@link #onCancelled()}并忽略结果。
* 如果你编写自己的实现,不要调用super.onCancelled(result)
*
* @param b 在{@link #doInBackground(Object [])}中计算的结果(如果有的话),可以为null
*/
@Override
protected void onCancelled(Boolean b) {
super.onCancelled(b);
}
/**
* Runs on the UI thread after {@link #cancel(boolean)} is invoked
* and {@link #doInBackground(Object[])} has finished.
* <p>
* 应用程序应优先覆盖{@link #onCancelled(Object)}。
* 此方法由{@link #onCancelled(Object)}的默认实现调用。
*/
@Override
protected void onCancelled() {
super.onCancelled();
}
}
对AsyncTask的操作方法
/**
* 启动该任务
* new SampleAsyncTask().execute();
*/
execute();
/**
* 尝试取消执行此任务。
* 如果任务已经完成,已经被取消或由于某种其他原因而无法取消,则此尝试将失败。
* 如果成功,并且在取消被调用时此任务尚未开始,则此任务不应该运行。
* 如果任务已经开始,那么 mayInterruptIfRunning 参数可以确定执行该任务的线程是否应该被中断以试图停止任务。
*
* final boolean cancel(boolean mayInterruptIfRunning)
* mayInterruptIfRunning:如果执行该任务的线程应该被中断,值为true; 否则,正在进行的任务被允许完成。
* 如果任务无法取消,通常是因为它已经正常完成,返回false;否则返回true
*/
cancel(mayInterruptIfRunning);
更多方法及详细说明查看官方API
需要了解的内容:
异步任务的四个步骤:
当执行异步任务时,任务将通过4个步骤:
- onPreExecute()在执行任务之前在UI线程上调用。 此步骤通常用于设置任务,例如通过在用户界面中显示进度条。
- doInBackground(Params …),在onPreExecute()完成执行后立即在后台线程上调用。 此步骤用于执行可能需要很长时间的后台计算。 异步任务的参数传递给此步骤。 计算结果必须由此步骤返回,并将被传递回最后一步。 此步骤也可以使用publishProgress(Progress …)发布一个或多个进度单位。 这些值在UI线程上,在onProgressUpdate(Progress …)步骤中发布。
- onProgressUpdate(Progress …),在调用publishProgress(Progress …)后在UI线程上调用。 执行时间未定义。 该方法用于在后台计算仍在执行时在用户界面中显示任何形式的进度。 例如,它可以用于对进度条进行动画处理或在文本字段中显示日志。
- onPostExecute(Result),在后台计算完成后在UI线程上调用。 后台计算的结果作为参数传递给该步骤。
取消任务
任何时候都可以通过调用cancel(boolean)来取消任务。 调用此方法将导致对isCancelled()的后续调用返回true。 调用此方法后,onCancelled(Object)将在doInBackground(Object [])返回后被调用,而不是onPostExecute(Object)。 为了尽可能快地取消任务,您应该始终从doInBackground(Object [])中定期检查isCancelled()的返回值(如果可能的话)。
Threading rules
这个类必须遵循一些线程规则才能正常工作:
- 必须在UI线程上加载AsyncTask类。 这是从JELLY_BEAN自动完成的。
- 必须在UI线程上创建任务实例。
- execute(Params…) 必须在UI线程上调用。
- 不要手动调用 onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…) 等方法。
- 该任务只能执行一次(如果尝试第二次执行,将抛出异常)。
实例
private class SampleAsyncTask extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {
progressDialog.show();//显示进度条对话框
}
@Override
protected Boolean doInBackground(Void... params) {
try {
while (true) {
int downloadPercent = i++;
publishProgress(downloadPercent);
Thread.sleep(200);
if (downloadPercent >= 100) {
break;
}
}
} catch (Exception e) {
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
//更新进度
progressBar.setProgress(values[0]);
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();//关闭对话框
//在这里提示下载
if (result) {
Toast.makeText(MainActivity.this, "下载成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "下载失败", Toast.LENGTH_SHORT).show();
}
}
}