AsyncTask源码深度解析

相信有很多同学都使用过AsyncTask,都知道onPreExecute,onProgressUpdate,onPostExecute方法均运行在主线程,doInBackground方法运行在子线程,可以在其中做一些耗时操作,在doInBackground方法中也可以调用publishProgress方法来更新UI(之后会调用到onProgressUpdate方法)。不知道大家有没有好奇过,为什么onPreExecute,onProgressUpdate,onPostExecute方法会运行在主线程呢?为什么doInBackground方法会运行在子线程呢?子线程调用publishProgress之后为什么能进而调用到主线程的onProgressUpdate?AsyncTask的使用真的有想象中那么安全吗?

不用多想,上面的几个问题,只有源码君才能告诉我们答案,所以,今天就带领大家从底层源码的角度深入剖析AsyncTask的内部实现机制,让大家对AsyncTask的工作原理有一个透彻的理解,大家是不是有点小期待了呢_

随着Android版本的变迁,AsyncTask在任务执行方面有着较大的差异。当一开始推出时,诸多任务是在一个单个的后台线程上串行执行的。从Android 1.6(API 4)开始,任务是在一个线程池中并发执行的。从Android3.0(API 11)开始,任务又变为在一个单个的线程上串行执行。本篇文章基于Android 4.1.2的源码进行分析。

首先来看AsyncTask的构造方法,代码如下:

 /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        mWorker = new WorkerRunnable() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

        mFuture = new FutureTask(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 occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

AsyncTask的构造方法中做的事情不多,就是初始化了两个成员变量,一个是WorkerRunnable类型的mWorker(WorkerRunnable实现了Callable接口),一个是FutureTask类型的mFuture,并且将mWorker作为构造参数传入mFuture中(对Callable,Future,FutureTask不太了解的同学请先移步至http://www.cnblogs.com/dolphin0520/p/3949310.html。

接下来我们去看AsyncTask的execute方法:

   /**
     * Executes the task with the specified parameters. The task returns
     * itself (this) so that the caller can keep a reference to it.
     * 
     * 

Note: this function schedules the task on a queue for a single background * thread or pool of threads depending on the platform version. 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 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being * executed on a single thread to avoid common application errors caused * by parallel execution. If you truly want parallel execution, you can use * the {@link #executeOnExecutor} version of this method * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings * on its use. * *

This method must be invoked on the UI thread. * * @param params The parameters of the task. * * @return This instance of AsyncTask. * * @throws IllegalStateException If {@link #getStatus()} returns either * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. * * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) * @see #execute(Runnable) */ public final AsyncTask execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }

execute方法竟然如此简单,仅仅是去调用了executeOnExecutor方法,我们赶紧去看一下executeOnExecutor方法:

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

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

在executeOnExecutor中,会先去判断当前AsyncTask的状态,如果不为PENDING,则抛异常。如果是PENDING,则将当前状态变为RUNNING,再去调用onPreExecute方法,由于此时的executeOnExecutor是运行在主线程的,所以onPreExecute方法也是运行在主线程的。之后再去对mWorker的mParams字段进行赋值并利用exec去执行mFuture。

我们基本可以推断出,任务执行的逻辑应该是在** exec.execute(mFuture)中,观察一下exec所对应的实参,是一个名为sDefaultExecutor**的Executor。这个sDefaultExecutor具体又是个什么样的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();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

我们发现,sDefaultExecutor其实就是一个SerialExecutor类型的常量,根据注释,SerialExecutor在执行任务时,会以一个串行的顺序,一次仅执行一个任务。由于SERIAL_EXECUTOR是一个常量,所以在一个特定的进程之内,串行化是具有全局效果的。

我们赶紧去看一下SerialExecutor的源代码,重点关注它是如何将任务的执行变成串行化的:

private static class SerialExecutor implements Executor {
        final ArrayDeque mTasks = new ArrayDeque();
        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中有两个很重要的成员变量,一个是ArrayDeque类型的mTasks,它是一个双端队列,存储了我们要执行的任务。还有一个是Runnable类型的mActive,初始值为null。

接下来就到了最为关键的execute方法了。execute方法接收的参数是一个Runnable对象,到这里,有的同学可能感觉到有点奇怪,在executeOnExecutor方法中,我们的Executor的execute方法接收的是一个FutureTask的对象,为什么两个execute方法接收的参数不匹配呢?其实是这样的,FutureTask本身就实现了RunnableFuture接口,RunnableFuture接口又继承了Runnable, Future接口,所以,FutureTask完全可以当作一个Runnable来用。我们继续去看SerialExecutor的execute方法,首先,新建了一个Runnable任务,在其run方法中有一个try,finally结构,try中直接去调用了传入的Runnable对象的run方法,finally中调用了scheduleNext方法。之后将这个新建立的Runnable任务放入双端队列的尾部。接下来会去判断mActive这个变量是否为null,若为null,执行scheduleNext方法。第一个任务到来时mActive肯定是null,所以肯定会去执行scheduleNext方法。我们再去看一下scheduleNext方法,它会从双端队列的队头取出一个元素,赋给mActive变量,如果此时的mActive变量不为null,则利用线程池THREAD_POOL_EXECUTOR来执行这个任务。

想象一下,如果在第一个任务执行的过程中,又来了第二个任务,会发生什么事情呢?首先,依然是对我们传入的Runnable任务进行重新封装,入队,之后会再去判断mActive是否为null,此时,mActive是不为null的,所以不会再去执行scheduleNext方法。那我们第二个任务就永远得不到执行了吗?其实不是的,我们回到之前的try,finally结构,我们发现,当try中的任务逻辑执行完成之后,会在finally中调用scheduleNext方法,也就是说,当我们第一个任务执行完成之后,会再去调用scheduleNext方法,在scheduleNext方法中,会从双端队列中取出第二个任务,交给线程池去执行,由此,任务的执行变成串行化了。

我们再回过头看一下SerialExecutor的execute方法,看到r.run()这一句。我们知道,execute方法的参数表面上是一个Runnable对象,实际上我们传递给它的是一个FutureTask对象,那么r.run()自然也是执行的FutureTask对象的run方法。FutureTask对象的run方法会去调用Sync内部类的innerRun方法,我们来看一下Sync内部类的innerRun方法:

void innerRun() {  
    if (!compareAndSetState(READY, RUNNING))  
        return;  
    runner = Thread.currentThread();  
    if (getState() == RUNNING) { // recheck after setting thread  
        V result;  
        try {  
            result = callable.call();  
        } catch (Throwable ex) {  
            setException(ex);  
            return;  
        }  
        set(result);  
    } else {  
        releaseShared(0); // cancel  
    }  
}  

在Sync内部类的innerRun方法中,会去调用callable的call方法。这个callable是什么呢?其实就是我们在AsyncTask构造方法中初始化的mWorker变量,我们再回顾一下mWorker的初始化代码:

mWorker = new WorkerRunnable() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

注意,此时的call方法是在子线程运行的。我们看到return postResult(doInBackground(mParams))这一句,终于,我们发现了doInBackground方法,由于当前的call方法是在子线程运行的,所以doInBackground方法也是在子线程运行的。

我们继续去看一下postResult方法:

private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(this, result));
        message.sendToTarget();
        return result;
    }

大家有没有感觉到这段代码很熟悉呢?没错,这就是我们Android的异步消息处理机制。首先,利用sHandler去获取一条消息,消息的what字段是MESSAGE_POST_RESULT,消息的obj字段是new AsyncTaskResult(this, result),AsyncTaskResult中封装了当前的AsyncTask任务以及需要传递的数据。之后调用message的sendToTarget方法将这条消息发送给sHandler。

我们看下sHandler的定义:

private static final 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是一个InternalHandler类型的常量。由于我们的AsyncTask只能在主线程初始化,所以sHandler在初始化时用的是主线程的Looper,其handleMessage方法自然也是运行在主线程的。在handleMessage方法中,首先取出消息的obj字段并强转为AsyncTaskResult类型,之后会去判断消息的what字段,如果是MESSAGE_POST_RESULT,则执行result.mTask.finish(result.mData[0])。其中result.mTask代表当前的AsyncTask对象,result.mData[0]代表doInBackground(mParams)的执行结果,我们继续去看一下AsyncTask的finish方法:

private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

finish方法的逻辑很简单,如果我们的任务已经通过调用cancel(boolean)方法取消了,那么会去执行onCancelled(result)方法,否则执行onPostExecute(result)方法。最后,会将当前的AsyncTask对象的状态置为FINISHED。由于之前的handleMessage方法是运行在主线程的,所以finish方法也是运行在主线程的,finish方法中的 onCancelled(result),onPostExecute(result)方法自然也是运行在主线程的。

在handleMessage中,还有一种what字段为MESSAGE_POST_PROGRESS的消息,那么什么时候会收到这种类型的消息呢,猜一下也知道,应该是我们调用publishProgress的时候。我们去看一下publishProgress的源代码:

 protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult(this, values)).sendToTarget();
        }
    }

当任务尚未取消时,会向sHandler发送一条what字段为MESSAGE_POST_PROGRESS,obj字段为new AsyncTaskResult(this, values)的消息。回到handleMessage方法,因为handleMessage方法是运行在主线程中的,所以onProgressUpdate方法也是运行在主线程中的。

好了,AsyncTask的源码到这里已经基本分析完毕了,下面向大家介绍的是AsyncTask使用的缺陷问题。

可能有同学会想,AsyncTask这么牛X的一个工具,能有啥缺陷?
其实,AsyncTask还真的有缺陷,而且,这个缺陷和Android的版本息息相关,在某些Android版本下,如果AsyncTask使用不慎,甚至有可能造成我们的应用程序崩溃。
前面我们曾经提及,从Android 1.6(API 4)开始,任务是在一个线程池中并发执行的。从Android3.0(API 11)开始,任务又变为在一个单个的线程上串行执行。问题就出在这个并发执行上,这个并发执行所使用的线程池,最大支持128个任务的并发,10个任务的等待。也就是说,同时执行138个任务是没问题的,但是同时执行139个任务则会抛出异常:java.util.concurrent.RejectedExecutionException。而在Android 1.6之下,Android3.0及其之上的版本中,所有的任务都是串行执行的,同时执行再多的任务都不会有问题。

看到这里,相信大家已经对AsyncTask的底层原理有了一个较为深入的理解了,想不到小小的AsyncTask的内部竟然隐藏着如此美妙的天地,着实值得我们去探索与回味啊~~~

参考:
http://blog.csdn.net/lmj623565791/article/details/38614699
http://blog.csdn.net/guolin_blog/article/details/11711405
http://www.cnblogs.com/dolphin0520/p/3949310.html

你可能感兴趣的:(AsyncTask源码深度解析)