AsyncTask 是一个封装了 ThreadPoolExecutor 和 Handler 机制的抽象类,其作用是方便开发者进行多个异步任务时无需手动在每个线程中都采用 Handler 机制来通知 UI 线程进行操作, 从而简化了多个异步任务与 UI 线程的通信的情况. 我们先留下几个问题, 在后续的源码探索的过程中进行验证: AsyncTask 类 为什么必须要在主线程中进行初始化 ? AsyncTask 在 Android 3.0 版本后有较大改动, 3.0 之前是并行执行, 3.0 之后是串行执行, 3.0 之前的基本可以忽略, 那么为什么 3.0 之后是串行执行的, 是如何串行的 ? 下面从这两个过程,分别从源码的角度探索 AsyncTask 的执行流程和内部原理: 1. new AsyncTask() 无论在主线程中调用到哪个构造方法, 最终都会调用到 这个 构造方法, 源码如下:
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams); // 注意此处调用了 doInBackground
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result); // 注意此处
}
return result;
}
};
mFuture = new FutureTask(mWorker) { // 注意此处传入了 mWorker
@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);
}
}
};
}
其中 WorkerRunnable 是 AsyncTask 一个内部类,源码如下:
private static abstract class WorkerRunnable
implements Callable { // 注意实现了 Callable接口
Params[] mParams;
}
2. execute
execute 方法会调用到 executeOnExecutor 方法,
源码如下:
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask 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(); // 注意此处调用了 onPreExecute
mWorker.mParams = params; // 注意此处把传入的 params 赋值给了 mParams
exec.execute(mFuture); // 1
return this;
}
我们看到, 当我们调用到 execute 方法后, 最终会用到 exec 这个变量执行之前在构造方法中初始化好的 future 对象, 我们跟进看看这个 exec 是什么:
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.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
到了这里我们明白了, exec 就是一个 SerialExecutor, 官方也明确的描述到了, 对于一个进程来说, 这个 AsyncTask 会串行执行所有提交的任务, 接下来, 我们看看 它的 execute 方法的实现:
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
Runnable mActive; // 1
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() { // 2
public void run() {
try {
r.run();
} finally {
scheduleNext(); // 3 注意此处
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
我们可以看到, 这个 SerialExecutor 的实现其实很简单, 其 execute 方法会把传入的 runnable 对象压入双向队列, 然后判断当前有没有正在执行的任务, 如果没有直接调用 scheduleNext 来开始执行一个任务, 然后真正执行任务的是由 THREAD_POOL_EXECUTOR , 这是一个自定义的线程池, 会根据设备的 cpu 数量设定核心池线程的数量, 到了这里我们可以解释一下为什么 3.0 之后的 AsyncTask 是串行执行任务的了:
首先说最重要的一点, AsyncTask 中的线程池都是
静态
的.
此后, 无论我们在主线程中创建多少个 AsyncTask 实例对象, 调用多少次 execute 方法, 其任务都会被提交到 SerialExecutor的 ArrayDeque 中进行排队.
比如我们同时提交了 5 个任务, 第一次提交时, mActive 为 null, 所以直接调用 ScheNext 方法执行第一个任务, 然后当第一个任务的 run 方法执行完后, 注意在注释 3 处的 finally 代码中, 又调用到了 scheduNext 方法来执行下一个任务, 如此反复, 便把所有提交的任务串行的执行完成.
接下来我们看看在执行的过成中和 主线程 进行通信的细节:
前面我们看到, 在 构造方法中会把 doInBackground 包装在一个 worker 中, 然后传入到了 FutureTask 中, 然后再把 FutureTask 传给了 exce 的 execute 的方法进行排队执行, 因此我们再看看 worker 的实现:
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams); //获取返回结果
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result); // 在 finally 中调用 postResult.
}
return result;
}
我们看到, 无论如何最后都会执行到 finally 中的 postResult, 跟进看看:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, // 1
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
很明显, 这里是获取 Handler 给主线程发送消息, 我们看看这个 getHandler 是什么:
private Handler getHandler() {
return mHandler;
}
读者还记不记得这个 handler 是在 构造方法中进行的初始化:
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
......
到这里, 这个构造方法中的 callbackLooper 一般情况下都为空, 所以会调用到 getMainHandler(), 我们看看:
private static Handler getMainHandler() { // 注意此处的 static
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper()); //1
}
return sHandler; // 静态成员变量
}
}
跟进看看这个 InternalHandler 是什么:
private static class InternalHandler extends Handler { // 注意 static 修饰了 class
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
result.mTask.finish(result.mData[0]); // 2
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
到了这里, 我们可以解释为什么 AsyncTask 必须要在主线程中进行初始化了:
InternalHandler 就是一个 handler, 但是注意被 static 修饰了, 也就意味着这个类会在 AsyncTask 加载的时候就会被进行加载,
并且之前获取 sHandler 这个静态成员变量的时候也是静态方法, 这些都表明了, 这个 sHandler 会在 AsyncTask 类加载的时候就会得到加载, 而要让 AsyncTask 正常工作, 它的 Handler 必须要和主线程的 looper 进行关联才能通信.
接下来, 我们看到之前由 postRresult 发送的 MESSAGE_POST_RESULT 消息在这里得到了处理, 这个 task 便是传入的 this, 也就是 AsyncTask 实例对象, 我们看看 finish 方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
很简单, 判断是否取消, 否则就调用 onPostExecute 方法进行最后的收尾工作, 我们可以对其进行重写.
总结:
我们通过对源码的探索, 解答了开头留下的两个问题:
串行执行的细节是由 serialExecutor 来进行排队处理, 真正的执行者还是一个 自定义的线程池, 这也是为什么 AsyncTask 不适合处理大量耗时较长的任务的原因 , 当然我们使用 executeOnExecutor 让 AsyncTask 来并行的处理任务.
必须要在主线程中加载的原因是因为其内部的 InternalHandler 是静态的, 其初始化过程必须要在主线程中执行才能和主线程进行关联.
如果有误, 欢迎评论指正!!
