这一篇博客主要分析一下Android O中AsyncTask相关的源码。
一、构造函数
AsyncTask是一个抽象类,实际使用时必须定义一个实现类。
在使用实现类时,我们需要创建出实例,会调用到AsyncTask的构造函数。
AsyncTask定义了几个构造函数:
//用的最多的
public AsyncTask() {
this((Looper) null);
}
//使用了@hide标签,仅Framework和系统应用可以直接使用
//指定回调的handler
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
//同样使用了@hide标签,仅Framework和系统应用可以直接使用
public AsyncTask(@Nullable Looper callbackLooper) {
//对于普通APK而言,callbackLooper为null, 于是将调用getMainHandler创建出主线程对应的Handler
//对应系统应用等,可以指定回调的handler,即回调可以运行在非主线程
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
//WorkerRunnable实现Callable接口
//参数类型为Params,接口函数call返回值的类型为Result
mWorker = new WorkerRunnable<Params, Result>() {
//容易看出,Worker负责封装传入参数,执行后得到结果
public Result call() throws Exception {
//记录当前AsyncTask是否执行
mTaskInvoked.set(true);
Result result = null;
try {
//AsyncTask执行的优先级为Background
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
//调用接口执行任务
//在构造函数中,mParams还没有实际的意义
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
//调用接口返回结果
postResult(result);
}
return result;
}
};
//创建一个FutureTask对象,构造函数的参数为一个callable对象
//当FutureTask执行时,会调用callable对象的call函数,在执行完毕后再回调done接口
//容易看出FutureTask封装了上文创建的Worker
mFuture = new FutureTask<Result>(mWorker) {
//Worker执行完毕后,回调done接口
@Override
protected void done() {
//主要作用其实是确保能够返回结果
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
从上面的代码可以看出,AsyncTask的构造函数中主要创建了以下几个对象:
发送回调信息的Handler;
封装调用参数,进行实际的处理并返回结果的Worker;
封装Worker,以便在执行后,进行收尾工作的FutureTask。
如果用命令模式来理解的话,那么AsyncTask中仅创建出了一个需要执行的命令。
这个命令还没有被添加到执行队列中,更别提实际的执行了。
在这部分的最后,我们看看上文中getMainHandler的代码:
//注意到, 此处利用主线程的Looper构建出静态单例对象InternalHandler
//说明对于普通APK而言,同一个进程中调用多个AsyncTask时,回调信息将由同一个Handler处理
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
二、DefaultExecutor
构造出AysncTask后,就可以调用接口开始实际的工作了。
从代码来看,AsyncTask提供了几个触发工作的接口,
我们还是以最常用的接口为例,进行分析:
//注解说明了此接口必须在主线程中调用
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
//注意到除了传入参数外,还传入了sDefaultExecutor
return executeOnExecutor(sDefaultExecutor, params);
}
在分析executeOnExecutor流程前,我们先看看sDefaultExecutor。
..............
//静态成员,进程共享,默认指向SERIAL_EXECUTOR
//可以调用AsyncTask的setDefaultExecutor进行设置
//因此,设置了volatile属性,保证并发的可见性
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
............
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
//SERIAL_EXECUTOR指向的对象为SerialExecutor
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
...........
/** @hide */
public static void setDefaultExecutor(Executor exec) {
sDefaultExecutor = exec;
}
从上述代码来看,sDefaultExecutor的类型默认为SerialExecutor。
可以通过调用setDefaultExecutor接口,来改变默认的行为。
不过该接口具有hide注解,意味着仅对Framework和系统应用可见。
因此对于普通APK而言,调用execute接口,
执行任务的线程池为默认为SerialExecutor。
现在我们来看看SerialExecutor的实现:
//定义于AysncTask中的内部类
............
private static class SerialExecutor implements Executor {
//队列中保存所有的任务
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
//mActive中保存当前正在执行的任务
Runnable mActive;
public synchronized void execute(final Runnable r) {
//execute函数被调用时,将runable对象加入到mTasks中
mTasks.offer(new Runnable() {
//当该runnable被执行后,调用scheduleNext
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
//当有新加入的任务时,若当前没有正在处理的任务,则直接调用scheduleNext接口
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//从队列中取出第一个任务
if ((mActive = mTasks.poll()) != null) {
//利用THREAD_POOL_EXECUTOR执行
//根据execute中的定义,我们知道一个runable的run方法结束后
//才会重新调用scheduleNext
//因此,虽然THREAD_POOL_EXECUTOR可以同时创建多个线程
//但AsyncTask还是一个接一个的处理任务
//如果将本函数的if改成while,就是并发处理了
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
.................
THREAD_POOL_EXECUTOR为AsyncTask加载时创建出的ThreadPoolExecutor:
.......................
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
//根据之前博客分析ThreadPoolExecutor的代码,
//调用allowCoreThreadTimeOut后,一旦超时,连核心线程也会被回收
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
.......................
从代码逻辑来看,AsyncTask实际上会一个接一个的处理任务,
因此THREAD_POOL_EXECUTOR实际上同一时间内仅会创建出一个后台线程。
AsyncTask采用这种方式定义线程池,是由历史原因决定的,可以参考如下注释:
* <h2>Order of execution</h2>
* <p>When first introduced, AsyncTasks were executed serially on a single background
* thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
* to a pool of threads allowing multiple tasks to operate in parallel. Starting with
* {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
* thread to avoid common application errors caused by parallel execution.</p>
* <p>If you truly want parallel execution, you can invoke
* {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with
* {@link #THREAD_POOL_EXECUTOR}.</p>
三、executeOnExecutor
了解完默认线程池相关的内容后,我们将视线拉回到executeOnExecutor接口。
注意到这个接口为public且没有hide,因此普通APK也可以直接调用。
//同样必须在主线程调用
@MainThread
//这个接口的强大在于可以指定Executor
//当利用前文的execute接口时,该参数对应SerialExecutor,将顺序执行任务
//直接调用这个接口时,就可以绕过SerialExecutor,并发执行任务了
//此处传入的参数也可以直接指定为AsyncTask.THREAD_POOL_EXECUTOR
//若使用THREAD_POOL_EXECUTOR,虽然AsyncTask可以并发处理,但所有AsyncTask仍共用线程池
//仍然不应该执行极其耗时的任务
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
//AsyncTask中定义了:private volatile Status mStatus = Status.PENDING;
//因此每个AsyncTask对象创建后,其状态都是PENDING
//这里就是注释叙述的,每个AsyncTask只能运行一次的原因
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)");
}
}
//AsyncTask开始运行后,状态变为RUNNING
mStatus = Status.RUNNING;
//回调子类的onPreExecute接口
//仍然是@MainThread
onPreExecute();
//将参数赋给构造函数中创建的WorkerRunnable
mWorker.mParams = params;
//将构造函数中创建的FutureTask加入到线程池的WorkQueue中
exec.execute(mFuture);
return this;
}
四、FutureTask
FutureTask被加入到ThreadPoolExecutor的WorkQueue后,
一旦被取出并执行,对应的run方法就会被调用:
public void run() {
//条件检查
.............
try {
//这个callable,就是AsyncTask构造函数中,构造FutureTask时传入的WorkerRunnable对象
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//调用WorkerRunnable的call方法
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
//WorkerRunnable执行完毕后,才调用FutureTask的set接口
//set接下来会调用finishCompletion函数,最终调用FutureTask的done函数
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
前文已经分析过,FutureTask实际执行时,调用的是WorkerRunnable的call接口。
WorkerRunnable中将调用doInBackground函数,在后台线程中完成任务,
在完成后调用postResult返回处理结果。
五、postResult
我们看看postResult函数的实现:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
//getHandler返回的是AsyncTask构造函数中创建的Handler对象
//默认为InternalHandler,运行在主线程中
//此处创建Message, Message中包含了后台线程的执行结果
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
我们看看InternalHandler的实现:
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
//处理返回结果
case MESSAGE_POST_RESULT:
// There is only one result
// 调用AsyncTask的finish函数
result.mTask.finish(result.mData[0]);
break;
//调用AsyncTask的publishProgress时,发送的是这个消息
case MESSAGE_POST_PROGRESS:
//回调更新进度
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
最后跟进AsyncTask的finish函数:
private void finish(Result result) {
//如果调用过cancel接口,则回调onCancelled
if (isCancelled()) {
onCancelled(result);
} else {
//正常情况下,回调onPostExecute接口
onPostExecute(result);
}
//更改当前AsyncTask的状态
mStatus = Status.FINISHED;
}
至此,AsyncTask被处理完毕。
从执行流程可以看出,AsyncTask中前、后台线程的通信,
实际上还是依靠了Handler。
六、收尾工作
前文提到过,WorkerRunnable的run方法执行完毕后,
最终将会调用FutureTask的done函数。
对于AsyncTask的流程而言,done函数主要调用了postResultIfNotInvoked。
private void postResultIfNotInvoked(Result result) {
//前面的代码提到过,WorkRunnable的call被执行后,mTaskInvoked已经置为了true
final boolean wasTaskInvoked = mTaskInvoked.get();
//因此,正常情况下不会再进行postResult的工作
if (!wasTaskInvoked) {
postResult(result);
}
}
此处主要是为了配合AsyncTask的cancel接口。
我们看看AsyncTask的cancel接口:
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
//调用FutureTask的cancel接口
return mFuture.cancel(mayInterruptIfRunning);
}
跟进FutureTask的cancel接口:
public boolean cancel(boolean mayInterruptIfRunning) {
//条件检查
.............
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
U.putOrderedInt(this, STATE, INTERRUPTED);
}
}
} finally {
//回调FutureTask的done接口,进入上文的postResultIfNotInvoked
//最终回调掉postResult
finishCompletion();
}
return true;
}
由于默认情况下,AsyncTask是依次处理的。
因此,当某个AsyncTask加入到队列后,可能在实际处理前被cancel掉。
此时,就会触发FutureTask的cancel接口,进入到AsyncTask中定义的done函数。
最终,通过postResultIfNotInvoked函数,利用Handler触发AsyncTask的onCancelled接口。
七、总结
本篇博客中,我们分析了AsyncTask的基本概念、使用方式及源码。
虽然在实际使用时,我们并不需要对其实现方式有深刻的理解,
但如果可能的话,掌握它的实现方式,可能会得到一些额外的启发。