AsyncTask 的工作原理

AsyncTask的工作原理

       为了分析 AsyncTask 的工作原理,从它的 execute 方法开始分析,execute 方法又会调用 executeOnExecutor 方法,它们的实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

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;
}

       在上面的代码中,sDefaultExecutor 是一个串行的 Executor(注意它不是线程池,只是实现了 Executor 接口),一个进程中所有的 AsyncTask 全部在这个串行的 Executor 中排队执行。在 executeOnExecutor 方法中,AsyncTask 的 onPreExecute 方法最先执行,然后 Executor 开始执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

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);
        }
    }
}

       从 SerialExecutor 的实现可以分析 AsyncTask 的排队执行过程。首先系统会把 AsyncTask 的 Params 参数封装为 FutureTask 对象,FutureTask 是一个并发类,在这里它充当了 Runnable 的作用。接着这个 FutureTask 会交给 SerialExecutor 的 execute 方法去处理,SerialExecutor 的 execute 方法首先会把 FutureTask 对象插入到任务列表 mTasks 中,如果这个时候没有正在活动的 AsyncTask 任务,那么就会调用 SerialExecutor 的 scheduleNext 方法来执行下一个 AsyncTask 任务。同时当一个 AsyncTask 任务执行完后,AsyncTask 会继续执行其它任务直到所有的任务都被执行为止,从这一点可以看出,在默认情况下,AsyncTask 是串行执行的。

       AsyncTask 中有两个 Executor(SerialExecutor 和 THREAD_POOL_EXECUTOR)和一个 Handler(IntentHandler),其中 SerialExecutor 用于任务的排队,而线程池 THREAD_POOL_EXECUTOR 用于真正地执行任务,IntentHandler 用于将执行环境从线程池切换到主线程。在 AsyncTask 的构造方法中有如下一段代码,由于 FutureTask 的 run 方法会调用 mWorker 的 call 方法,因此 mWorker 的 call 方法最终会在线程池中执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mWorker = new WorkerRunnable<Params, Result>() {
    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);
        }
        return result;
    }
};

       在 mWorker 的 call 方法中,首先将 mTaskInvoked 设为 true,表示当前任务已经被调用过了,然后执行 AsyncTask 的 doInBackground 方法,接着将其值传递给 postResult 方法,它的实现如下所示:

1
2
3
4
5
6
7
private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

       在上面的代码中,postResult 方法会通过 sHandler 发送一个 MESSAGE_POST_RESULT 的消息,这个 sHandler 的定义如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static InternalHandler sHandler = new InternalHandler();

private static class InternalHandler extends Handler {
    @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]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
            break;
        }
    }
}

       可以发现,sHandler 是一个静态的 Handler 对象,为了能够将执行环境切换到主线程,这就要求 sHandler 这个对象必须在主线程中创建。由于静态成员会在加载类的时候进行初始化,因此这就变相要求 AsyncTask 的类必须在主线程中加载,否则同一个进程中的 AsyncTask都将无法正常工作。sHandler 收到 MESSAGE_POST_RESULT 这个消息后会调用 AsyncTask 的 finish方法,如下所示:

1
2
3
4
5
6
7
8
private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

       AsyncTask 的 finish 方法的逻辑比较简单,如果 AsyncTask 被取消执行了,那么就调用 onCancelled 方法,否则就会调用 onPostExecute 方法,可以看到 doInBackground 的返回结果会传递给 onPostExecute 方法,到这里 AsyncTask 的整个工作过程就分析完毕了。

       通过分析 AsyncTask 的源码,可以进一步确定,从 Android3.0 开始,默认情况下 AsyncTask 的确是串行执行的,在这里通过一系列实验来证实这个判断。

       请看如下代码,代码很简单,就是单击按钮的时候同时执行5个 AsyncTask 任务,每个 AsyncTask 会休眠3s来模拟耗时操作,同时把每个 AsyncTask 执行结束的时间打印出来,这样就可以观察出 AsyncTask 到底是串行执行还是并行执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override  
public void onClick(View v) {  
    if (v == mButton) {  
        new MyAsyncTask("AsyncTask#1").execute("");  
        new MyAsyncTask("AsyncTask#2").execute("");  
        new MyAsyncTask("AsyncTask#3").execute("");  
        new MyAsyncTask("AsyncTask#4").execute("");  
        new MyAsyncTask("AsyncTask#5").execute("");  
}  
  
}  
  
private static class MyAsyncTask extends AsyncTask<StringIntegerString{  
    private String mName = "AsyncTask";  
    public MyAsyncTask(String name) {  
        super();  
        mName = name;  
    }  
  
    @Override
    protected String doInBackground(String... params) {        try {  
            Thread.sleep(3000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        return mName;  
    }  

    @Override  
    protected void onPostExecute(String result) {  
        super.onPostExecute(result);
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Log.e(TAG, result + "execute finish at " + df.format(new Date()));  
    }  
}

       在 Android 6.0 的设备上运行程序,看下面的运行结果:

       从下面 Log 可以看出,5个 AsyncTask 共耗时15s(AsyncTask#5的时间减去 AsyncTask#1 的时间再加上 AsyncTask#1 打印 Log 开始睡眠的3秒,即18-6+3=15)且时间间隔为3s,很显然是串行执行的。

1
2
3
4
5
com.example.wy521angel.asynctasktest E/MainActivity: AsyncTask#1execute finish at2018-01-12 09:41:06
com.example.wy521angel.asynctasktest E/MainActivity: AsyncTask#2execute finish at2018-01-12 09:41:09
com.example.wy521angel.asynctasktest E/MainActivity: AsyncTask#3execute finish at2018-01-12 09:41:12
com.example.wy521angel.asynctasktest E/MainActivity: AsyncTask#4execute finish at2018-01-12 09:41:15
com.example.wy521angel.asynctasktest E/MainActivity: AsyncTask#5execute finish at2018-01-12 09:41:18

       Android 3.0 以下的设备暂未测试。

       为了让 AsyncTask 可以在 Android 3.0 及以上的版本上并行,可以采用 AsyncTask 的 executeOnExecutor 方法,需要注意的是这个方法是 Android3.0 新添加的方法,并不能在低版本上使用,如下所示:

1
2
3
4
5
6
7
8
9
10
11
@TargetApi(Build.VERSION_CODES.HONEYCOMB)  
@Override  
public void onClick(View v) {  
    if (v == mButton) {  
        new MyAsyncTask("AsyncTask#1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");  
        new MyAsyncTask("AsyncTask#2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");  
        new MyAsyncTask("AsyncTask#3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");  
        new MyAsyncTask("AsyncTask#4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");  
        new MyAsyncTask("AsyncTask#5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");  
}  
}

       在 Android 6.0 的设备上运行程序,运行结果如下:

1
2
3
4
5
com.example.wy521angel.asynctasktest E/MainActivity: AsyncTask#1execute finish at2018-01-12 10:12:59
com.example.wy521angel.asynctasktest E/MainActivity: AsyncTask#2execute finish at2018-01-12 10:12:59
com.example.wy521angel.asynctasktest E/MainActivity: AsyncTask#3execute finish at2018-01-12 10:12:59
com.example.wy521angel.asynctasktest E/MainActivity: AsyncTask#4execute finish at2018-01-12 10:12:59
com.example.wy521angel.asynctasktest E/MainActivity: AsyncTask#5execute finish at2018-01-12 10:12:59

       很显然,我们的目的达到了,成功的让 AsyncTask 在6.0的手机上并行起来了。

代码见:AsyncTask串行并行测试

参考资料:
《Android 开发艺术探索》任玉刚 第11章 11.2 11.2.2 AsyncTask的工作原理

Fork me on GitHub