美文网首页Android知识
AsyncTask源码深度解析

AsyncTask源码深度解析

作者: 伐冰 | 来源:发表于2016-05-07 12:21 被阅读71次

    相信有很多同学都使用过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<Params, Result>() {
                public Result call() throws Exception {
                    mTaskInvoked.set(true);
    
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    return postResult(doInBackground(mParams));
                }
            };
    
            mFuture = new FutureTask<Result>(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.
         * 
         * <p>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.
         *
         * <p>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<Params, Progress, Result> execute(Params... params) {
            return executeOnExecutor(sDefaultExecutor, params);
        }
    

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

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

    在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<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中有两个很重要的成员变量,一个是ArrayDeque<Runnable>类型的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<Params, Result>() {
                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<Result>(this, result));
            message.sendToTarget();
            return result;
        }
    

    大家有没有感觉到这段代码很熟悉呢?没错,这就是我们Android的异步消息处理机制。首先,利用sHandler去获取一条消息,消息的what字段是MESSAGE_POST_RESULT,消息的obj字段是new AsyncTaskResult<Result>(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<Progress>(this, values)).sendToTarget();
            }
        }
    

    当任务尚未取消时,会向sHandler发送一条what字段为MESSAGE_POST_PROGRESS,obj字段为new AsyncTaskResult<Progress>(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源码深度解析

        本文链接:https://www.haomeiwen.com/subject/ysphrttx.html