1 引言
在前面的一片文章AsyncTask简介(一) 基本概念和用法中,我们对AsyncTask的使用方法进行了简单的介绍。
本文从源码的角度对AsyncTask进行剖析,学习AsyncTask的设计思想和原理,明白了设计思想和原理。便会对AsyncTask有更深刻的认识。
2 AsyncTask的构造函数
使用AsyncTask,一般来说分三步,
1. 重写AsyncTask的各回调方法
2. 构建AsyncTask实例
3. 调用AsyncTask的execute方法
所以我们先来学习一下AsyncTask的实例是如何被构建的。
AsyncTask的构造函数如下:
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
...
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
...
}
};
}
构造函数很简单,首先构建了一个WorkerRunnable类型的对象mWorker,并以mWorker为参数构建了一个FutureTask对象mFuture。
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
WorkerRunnable是一个抽象类,仅在Callable接口的基础上增加了mParams属性。
mWorker是一个Callable对象,mFuture是用mWorker构建的FutureTask对象。很显然mWorker中的内容将会被提交到一个线程池中执行。
mWorker的call方法中有一行代码:
return postResult(doInBackground(mParams));
我们在这里见到了我们熟悉的doInBackground。
虽然我们还没有学习整个AsyncTask的代码,但是看到这里,我们几乎可以断定AsyncTask的执行架构是下面这样。
关于线程池,Callable,FutureTask等概念,大家可以百度Java多线程相关的知识。在这里,大家只需要知道,mFuture是对mWorker的一次封装,mFuture将会被提交到线程池执行,最终mWorker里的call方法将会在线程池中的某个线程中被执行。
3 AsyncTask的execute方法
在第2节中,我们已经构建完了AsyncTask实例,下面只需要在UI线程中调用execute即可运行异步任务。所以再来看一下execute函数:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {
...
onPreExecute();
...
mWorker.mParams = params;
exec.execute(mFuture);
...
}
execute调用 executeOnExecutor(sDefaultExecutor, params);
executeOnExecutor(sDefaultExecutor, params)的方法体中重点关注三个步骤:
1. onPreExecute(),可以看出,在调用了execute以后,onPreExecute方法得到了立即执行,所以我们在这里做AsyncTask的准备工作。因为execute是在UI线程中调用,所以onPreExecute也是在UI线程中调用的,可以在onPreExecute方法中安全的更新UI。
2. mWorker.mParams = params。params是我们在调用execute的时候传递的参数,mWorker持有了这个参数列表。
3. exec.execute(mFuture),exec实际上就是sDefaultExecutor,调用sDefaultExecutor的execute方法执行mFuture。这一步也是AsyncTask的核心的步骤了,我们下面详细分析一下这一步。
4 AsyncTask的执行器sDefaultExecutor
我们在第三节中讲到,调用AsyncTask的静态方法execute,最终都将调用sDefaultExecutor的execute方法。我就先来认识一下sDefaultExecutor:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new 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);
}
}
}
sDefaultExecutor是一个SerialExecutor实例。SerialExecutor实现了Executor接口。Executor也是Java的current包中的一员。他的内容非常简单,只有execute函数接口。
SerialExecutor 包含两个属性和两个方法:
1.属性mTasks, 一个双向队列,队列中的元素是Runnable对象:ArrayDeque< Runnable > mTasks,mTask是execute的任务队列,每一个任务是一个Runnable对象。
2.属性mActive,一个Runnable对象,任务队列中当前正在被执行的任务。
3.方法execute(Runnable r),我们在第三节中讲到调用AsyncTask实际上就是调用sDefaultExecutor.execute(mFuture)。mFuture是一个FutureTask对象,封装了Callable对象mWorker,由于FutureTask同时实现了Runnable和Callable接口,可以将Callable对象转换成Runnable对象。所以,实际上sDefaultExecutor.execute的参数是之前的Callable对象mWorker。在execute函数中又新建了一个Runnable对象,该Runnable对象的run方法,就是mWorker的call方法,但是增加了finally代码块,在执行完run方法以后,将自动执行scheduleNext方法。在构建完该Runnable对象以后,将此Runnable对象放入任务队列mTask。若第一次执行AsyncTask,mActive为空,调用scheduleNext方法。
4. 方法scheduleNext,从任务列表mTask中取出一个任务mActive,并调用THREAD_POOL_EXECUTOR.execute(mActive)。
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
THREAD_POOL_EXECUTOR是一个线程池。所以mActive将会被放到这个线程池中执行。
讲到这里,我们已经把AsyncTask的执行框架梳理完毕了。我们还是对照着图来说明一下。
sDefaultExecutor是一个SerialExecutor对象,SerialExecutor类中有一个任务队列mTask,队列成员是待执行的任务,他们都是Runnable对象。而我们在构建AsyncTask实例的时候传入的mWorker将会通过FutureTask类转换成Runnable对象放入任务队列mTask。
**首次执行execute的时候,将调用scheduleNext函数,将mTask队列的头放入已经准备好的线程池中THREAD_POOL_EXECUTOR中执行。执行任务就是执行该Runnable的run方法(由mWorker的call方法适配而来)。所以我们在构建mWorker对象的时候的postResult(doInBackground)方法将在线程池中被执行。
到这里我们看到为什么doInBackground是如何在后台线程中被调用的。**
我们还可以看到sDefaultExecutor 是一个静态对象,所以不管调用任何一个AsyncTask的execute方法,都将在同一个实例sDefaultExecutor上调用execute方法。而且提交到SerialExecutor的任务队列的元素是被串行执行的,当前一个任务元素执行完以后,下一个任务元素才可以被执行。这就是为什么不能在AsyncTask中做特别耗时操作的原因.
5 后台线程是如何与主线程通信的?
我们貌似已经讲完了AsyncTask的执行流程。但是只涉及到了onPreExecute和doInBackground。
我们接下来就看一下onUpdateProgress和onPostExecute又是如何执行的。
5.1 AsyncTask的Handler
我们都知道AsyncTask是对Handler和Thread的封装。Thread,我们前面已经看到了,是将doInBackground封装在FutureTask中,提交到线程池THREAD_POOL_EXECUTOR。那Handler呢?
在AsyncTask中有一个属性sHandler:
从其构造函数中可以看出,InternalHandler被绑定到 Looper.getMainLooper()这个Looper上,这表明InternalHandler是绑定到主线程上的。handleMessage方法将在主线程中被调用。
而若想获得InternalHandler实例
private static InternalHandler sHandler;
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
5.2 利用AsyncTask中的Handler进行线程间通信
sHandler是一个单例,通过getHandler方法获取。所有的AsyncTask公用同一个sHandler。
InternalHandler只处理两个消息:
1) MESSAGE_POST_PROGRESS:
当publishProgress被调用的时候
protected final void publishProgress(Progress... values) {
...
getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
将会调用sHandler发送一个MESSAGE_POST_PROGRESS消息,InternalHandler接收到这个消息以后在handleMessage中调用result.mTask.onProgressUpdate(result.mData)进行处理,这样用户重写的onProgressUpdate将会被调用。我们前面说过InternalHandler是绑定到主线程中的,所以onProgressUpdate是主线程中被调用的。可以安全更新UI。
2) MESSAGE_POST_RESULT:
在第2节中介绍mWorker的call方法:
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
...
return postResult(doInBackground(mParams));
}
};
我们前面已经介绍过call方法将会被放到线程池中执行。doInBackground将由AsyncTask的子类重写。其返回值将调用postResult方法。
private Result postResult(Result result) {
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
postResult方法将调用sHandler,发送一个MESSAGE_POST_RESULT消息
hanleMessage在处理MESSAGE_POST_RESULT则会调用
result.mTask.finish(result.mData[0]);
private void finish(Result result) {
...
onPostExecute(result);
...
}
所以线程池执行完doInBackground以后,会调用postResult,postResult则会借助于InternalHandler发送一个MESSAGE_POST_RESULT消息,hanleMessage处理了这个消息,调用onPostExecute,InternalHandler是绑定到主线程中的,所以onPostExecute是主线程中被调用的。可以安全更新UI。
6 用AsyncTask执行Runnable对象
AsyncTask还提供一个简单易用的使用方法,无需创建AsyncTask实例,使用AsyncTask的静态方法。
public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}
调用execute方法直接执行一个Runnable对象。
直接将一个Runnable对象放入sDefaultExecutor的任务队列mTask中。由线程池THREAD_POOL_EXECUTOR直接执行。
比较一下AsyncTask的两种执行异步任务的方法。
1. 重写doInBackground方法。将doInBackground方法封装在一个Callable对象中,然后经由FutureTask转换成一个Runnable对象,再放入任务队列mTask中,提交到THREAD_POOL_EXECUTOR执行。缺点,稍显繁琐。优点,可以对任务执行进行初始化,执行过程跟踪显示,获取执行结果。
2. 调用静态方法,直接执行一个Runnable对象。Runnable对象被直接放入mTask,提交到THREAD_POOL_EXECUTOR执行。缺点,无法跟踪执行过程,无法获取执行结果。优点,代码简单。适合不关心执行过程和结果的异步任务。
7 AsyncTask的并行执行
我们前面讲过提交到AsyncTask的异步任务,都是放到sDefaultExecutor的任务队列mTask中,sDefaultExecutor顺序的读取mTask中的任务,串行的执行其中的任务。sDefaultExecutor是在Honeycomb版本以后才引入的,在此之前AsyncTask中的任务是并行执行的。
那么如果我们想并行的执行AsyncTask中的任务的时候怎么办?直接以THREAD_POOL_EXECUTOR为参数,调用executeOnExecutor(Executor exec, Params… params)。每次调用executorOnExecutor,任务都将立即被提交THREAD_POOL_EXECUTOR,并得到运行。
后台异步任务的并行执行会引发很多比较难处理的错误。所以Android在Honeycomb以后引入AsyncTask串行执行机制。也建议大家使用AsyncTask的默认的串行执行的行为,如果想要获取对后台任务执行更多的控制力,可以使用Handler+Thread。