AsyncTask的本质实际仍是传统的线程,它封装了Thread和Handler,底层用到了线程池。它在线程池中执行后台任务,支持把执行的进度和最终结果传递给主线程并在主线程中更新UI。但是AsyncTask不适合执行特别耗时的后台任务,对于特别耗时的任务,建议直接使用线程池(原因请参考下面的工作原理分析)。
使用方法
AsyncTask是一个抽象的泛型类,它的泛型参数有三个:
public abstract class AsyncTask<Params, Progress, Result>
Params:doInBackground方法接收的参数类型,即执行这个AsyncTask时传入的参数,比如需要执行下载任务,这里可能就是URL类型。
Progress:onProgressUpdate方法接收的参数类型,即后台任务执行进度的类型,比如可以设置为Integer。
Result:onPostExecute方法接收的参数类型,即后台任务执行完毕后返回的结果类型,比如后台请求了网络接口,服务端返回了一个Json格式串,这里就可以设为Json类型。
如果不需要设置某个参数,可以将其写为Void。
AsyncTask提供了5个核心的方法供子类去复写,其中doInBackground必须要实现,其他可酌情而定:
onPreExecute(),在主线程中执行,在异步任务执行之前,此方法会被调用,一般用于做一些准备工作。
doInBackground(Params[] params),在线程池中执行,params表示异步任务的输入参数。在这个方法中可以调用publishProgress方法来更新任务进度,调用后onProgressUpdate方法就会被回调。另外,此方法的返回结果会传递给onPostExecute。
onProgressUpdate(Progress[] values),在主线程中执行,当后台异步任务进度发生改变时,可通过在doInBackground里调用publishProgress方法使该方法被调用,可以在这里面更新进度条UI。
onPostExecute(Result result),在主线程中执行,在后台异步任务执行完毕之后,此方法会被调用,可以在这里面执行一些收尾工作。
onCancelled(),在主线程中执行,在后台异步任务被取消时(开发者调用AsyncTask的cancel()方法),该方法会被回调。可以在doInBackground里通过if(isCancelled())来判断任务是否已被取消。
这几个方法的执行顺序和流程是:
demo
public class MainActivity extends ActionBarActivity {
private URL url;
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
try {
url = new URL("...");
DownloadTask downloadTask = new DownloadTask();
downloadTask.execute(url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
class DownloadTask extends AsyncTask<URL, Integer, String>{
@Override
protected void onPreExecute() {
Toast.makeText(mContext, "开始下载", Toast.LENGTH_SHORT).show();
}
@Override
protected String doInBackground(URL... params) {
//此处省略了具体的下载代码逻辑,返回值是下载文件的存储路径
return Downloader.download(params[0]);
}
@Override
protected void onPostExecute(String path) {
if(TextUtils.isEmpty(path)){
Toast.makeText(mContext, "文件下载失败", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(mContext, "文件下载完成,存储位置:" + path, Toast.LENGTH_LONG).show();
}
}
}
}
条件限制
(原因请参考下面的工作原理分析)
AsyncTask的对象必须在主线程中创建
execute方法必须在主线程中调用
不要手动去调用onPreExecute、doInBackground、onProgressUpdate、onPostExecute、onCancelled
一个AsyncTask只能execute一次,否则会报运行时异常
Android 1.6之前,AsyncTask串行执行任务;Android 1.6 - 3.0前,AsyncTask并行执行任务;从Android 3.0开始,AsyncTask又恢复串行执行任务(如果希望它可以并行执行任务,需调用executeOnExecutor方法)
工作原理分析
一个AsyncTask的execute()方法的源码如下:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
这里的sDefaultExecutor是一个串行的线程池,一个进程中所有的AsyncTask会在这个串行的线程池里排队执行。
接着跟踪源码:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
这里就看出了为什么AsyncTask不能重复execute的原因,会抛出异常。这里会首先执行onPreExecute,然后就会调用线程池的execute方法了。
下面通过SerialExecutor来分析一下线程池的的执行过程:
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
首先系统会把AsyncTask的Params参数封装为FutureTask对象,然后由SerialExecutor 的execute方法去处理,即我们之前看到的exec.execute(mFuture); 这行代码。FutureTask是一个并发类,相当于是一个Runnable。SerialExecutor 的execute方法接收到以后,会把它插入任务队列mTasks里,如果这个时候没有正在活动的AsyncTask,那么就会调用scheduleNext方法执行下一个AsyncTask。从这里可以看出,AsyncTask是串行执行的。
AsyncTask有两个线程池:SerialExecutor 和 THREAD_POOL_EXECUTOR,还有一个Handler:InternalHandler,其中SerialExecutor用于处理任务的排队,而THREAD_POOL_EXECUTOR真正的去执行任务,InternalHandler负责将执行环境切换到主线程。
FutureTask的run方法会调到mWorker的call方法,而mWorker的call方法会在线程池中执行:
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
......
这里可以看出就会调用doInBackground方法了,然后返回结果传给postResult方法:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
sHandler(InternalHandler对象)会发送MESSAGE_POST_RESULT消息。为了能使执行环境切换到主线程,InternalHandler对象必须在主线程中创建,所以也就变相要求AsyncTask的实例也必须在主线程中创建。
AsyncTask不适合执行特别耗时的后台任务,也是由于它的不可重复性和不可断点续传性。