学习或翻译自官方文档:https://developer.android.com/reference/android/os/AsyncTask.html
一、概述
使用过AsyncTask开发的都知道,AsyncTask使得开发者更加容易和正确的使用UI线程,它允许我们不创建新线程或者handler而实现执行后台操作并更新UI。
需要注意的是:理想情况下,AsyncTasks应该用于短操作(最多几秒钟)。如果需要保持线程长时间运行,强烈建议使用java.util.concurrent包提供的各种API,例如Executor,ThreadPoolExecutor和FutureTask。
AsyncTask由3种通用类型定义,称为Params,Progress和Result,以及4个步骤,称为onPreExecute,doInBackground,onProgressUpdate和onPostExecute。
二、用法
1.AsyncTask必须被子类化才能使用。子类将覆盖至少一个方法(doInBackground(Params ...)),并且通常会覆盖第二个方法(onPostExecute(Result))。
下面是官方的一个例子
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { 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; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } }
调用非常简单:在需要的地方如下使用即可
new DownloadFilesTask().execute(url1, url2, url3);
2.AsyncTask的泛型类型
2.1 params:执行时发送到任务的参数的类型
2.2 Progress:在后台计算期间发布的进度单位的类型
2.3 result:后台计算的结果的类型
并非所有类型都会被使用。要将类型标记为未使用,只需使用类型Void:
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
3.当执行异步任务时,任务通过4个步骤:
3.1 onPreExecute() 在执行任务之前在UI线程上调用。此步骤通常用于设置任务,例如在用户界面中显示进度条
3.2 doInBackground(Params...) 在onPreExecute()执行后立即在后台线程上调用。此步骤用于执行可能需要很长时间的后台计算。异步任务的参数传递到此步骤。计算的结果必须通过此步骤返回,并将返回到最后一步。还可以在此步骤内调用publishProgress(Progress ...)发布一个或多个进度单位。这些值会传到在UI线程上执行的onProgressUpdate(Progress ...)步骤中。
3.3 onProgressUpdate(Progress...) publishProgress(Progress ...)被调用之后该方法在UI线程上调用。执行的时间未定义。此方法用于在后台计算仍在执行时在用户界面中显示任何形式的进度。例如,它可以用于动画进度条或在文本字段中显示日志。
3.4 onPostExecute(Result) 在后台计算完成后在UI线程上调用。后台计算的结果作为参数传递到该步骤。
综上:除doInBackground(Params...)在后台线程中执行,另外3个方法都运行在UI线程中。
4.取消任务
任务可以通过调用cancel(boolean)随时取消。调用此方法将导致isCancelled()返回true。同时在doInBackground(Object [])执行结束后,会调用onInCancelled(Object),而不是onPostExecute(Object)。为了确保尽快取消任务,应该始终在doInBackground(Object [])中定期检查isCancelled()的返回值(例如在循环内),以便尽早退出后台任务。
5.使用规则
5.1 AsyncTask类必须在UI线程上加载。
5.2 任务实例必须在UI线程上创建。
5.3 execute(Params ...)必须在UI线程上调用。
5.4 不要手动调用onPreExecute(),onPostExecute(Result),doInBackground(Params ...),onProgressUpdate(Progress ...)。
5.5 任务只能执行一次(如果尝试第二次执行,则会抛出异常)。
6.AsyncTask保证所有回调调用都是线程同步的,以下操作在没有显式同步的情况下是安全的。
6.1 在构造函数或onPreExecute()中设置成员字段,并在doInBackground(Params ...)中引用它们。
6.2 在doInBackground(Params ...)中设置成员字段,并在onProgressUpdate(Progress ...)和onPostExecute(Result)中引用它们。
7.特别注意
AsyncTask提供公共方法:boolean cancel (boolean mayInterruptIfRunning)
该方法尝试取消此任务的执行。如果任务已完成、已取消或由于某些原因无法取消,则此尝试将失败。如果任务在调用cancel时未启动,则此任务不应运行。如果任务已经启动,则mayInterruptIfRunning参数确定是否应该中断执行此任务的线程以尝试停止任务。
实际测试,该方法不会按照预期随时结束任务。实际测试:调用cancel()方法后,doInBackground方法仍然在执行。所以为了确保尽快取消任务,应该始终在doInBackground(Object [])中定期检查isCancelled()的返回值(例如在循环内),以便尽早退出后台任务。