AsyncTask源码解析o=w=o

作者: 澄祯 | 来源:发表于2017-07-11 10:47 被阅读40次

    今天学习了AsyncTask的源码。打算把目前学到的写下来记录一下。可能有很多地方我还掌握得不太好,不过一步步来嘛。先理解着记啦。

    先稍微复习一下AsyncTask里面重要的一些方法吧~

    1. onPreExecute()
      该方法将在执行实际的后台操作前被UI 线程调用。可以在该方法中做一些准备工作,比如显示“正在加载”的进度条。
    
    2. doInBackground(Params...)
      将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。
      比如从网络拉取预览图, 每拉取一张以后, 可以调用 publishProgress方法来更新一张默认的图片。
    
    3. onProgressUpdate(Progress...)
       在publishProgress方法被调用后,这个方法将被UI线程调用, 用于更新进度等界面显示。比如调用这个方法更新一张图片。
    
    4. onPostExecute(Result)
       在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread. 
    

    今天的顺序,是打算由表及里,从AsyncTask调用的最后一个方法开始,追本溯源,去探寻这个类具体的实现方法。

    @MainThread
        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;
        }
    
    // TIP: mWorker是一个自定义的类,由于实现了Callable接口,因此可以被当做Callable类型的变量使用
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
            Params[] mParams;
        }
    
    /* Callable接口类似于Runnable,但是Runnable不会返回结果,并且无法抛出返回结果的异常。
       而Callable功能更强大一些,被线程执行后,可以通过Future对象拿到异步计算的结果(即可以拿到返回值)。*/
    

    可以看到,这个方法是在主线程中调用的。它在第一行做了判断,只有当目前任务的状态为等待,也就是初次创建尚未执行时,才能正确执行。否则将会抛出异常。(这里也能看出,一个AsyncTask对象只能被执行一次。否则会报错)。

    接下来,先修改任务状态为运行,再调用onPreExecute()方法中做一些UI的准备工作(如显示几张默认的预览图,或者显示进度条等),之后把mWorker参数的mParams变量设为声明AsyncTask类时传入的Params变量(即为第一个参数)。如果参数是null,可以忽略这一局。

    最后这一步很重要,调用了exec的execute()方法。

    从该方法传入的参数中可以看出,exec是第一个参数,是一个Executor类型的变量。Executor是一个工具,使用线程池来管理线程,可以重复利用已经创建出来的线程而不是每次都必须新创建线程,节省了一部分的开销。那么,这一句代表程序在子线程启动了mFuture任务。

    那么,到底什么是mFuture呢?我们可以跟踪它,在这个类前面的声明和方法中找到答案。

    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture
    
    public AsyncTask() {
            mWorker = new WorkerRunnable<Params, Result>() {
                public Result call() throws Exception {
                    mTaskInvoked.set(true);
    
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    Result result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                    return postResult(result);
                }
            };
    
            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 occurred while executing doInBackground()",
                                e.getCause());
                    } catch (CancellationException e) {
                        postResultIfNotInvoked(null);
                    }
                }
            };
        }
    

    可以看出,mFuture是一个FutureTask类型的成员变量,并且在构造方法里得到初始化。因为FutureTask类实现了RunnableFuture接口,所以能够被传入参数类型要求为Runnable的execute方法。

    这时,我们需要留意一个事情:mWorker变量是用来做什么的?我们发现,WorkRunnable的call()被重写,在mWorker的call()方法中,启动了doInBackground()方法,可是我们知道,这个方法是用来在子线程中处理耗时操作的。它是怎么跑到子线程里面去的呢?
    接着往下看,在mFuture对象的构造函数中,传入了mWorker参数,又因为mFuture是在子线程中被启动的,这就说明,在FutureTask内部用到了mWorker,所以mWorker的call()方法里里可以调用只能在子线程调用的doInBackground()方法。

    我们深入到FutureTask类里面去看一下,里面是怎么用到mWorker的,以此来印证上文的猜测。

    // 在FutureTask类中
    private Callable<V> callable;
    
    public FutureTask(Callable<V> callable) {
            if (callable == null)
                throw new NullPointerException();
            this.callable = callable;
            this.state = NEW;       // ensure visibility of callable
        }
    
    public void run() {
            if (state != NEW ||
                !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
                return;
            try {
                Callable<V> c = callable;
                if (c != null && state == NEW) {
                    V result;
                    boolean ran;
                    try {
                        result = c.call();
                        ran = true;
                    } catch (Throwable ex) {
                        result = null;
                        ran = false;
                        setException(ex);
                    }
                    if (ran)
                        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);
            }
        }
    

    在构造方法中,mWorker传入FutureTask类之后,被赋给了该类的callable成员变量。同时把标志状态的变量state初始化为NEW。

    // 在使用过程中,state可能的变化形式
    Possible state transitions:
         * NEW -> COMPLETING -> NORMAL
         * NEW -> COMPLETING -> EXCEPTIONAL
         * NEW -> CANCELLED
         * NEW -> INTERRUPTING -> INTERRUPTED
    

    在run()方法里,我们一步步来分析。如果state此时不为NEW,即该任务已经被启动过,那么直接return,不再进行下面的操作。这也直接反映了,为什么一个FutureTask对象只能执行一次,多次调用run()方法无效。

    /** Once the computation has completed, the computation cannot be restarted
     *  or cancelled (unless the computation is invoked using
     *  {@link #runAndReset}). */
    

    然后,把callable赋给一个新的变量c,并调用c的call()方法执行任务,这个call()方法就是用来执行我们要完成的耗时任务。如果执行时没有抛出异常,那么任务执行完毕后,调用set()方法设置输出结果。输出结果的具体过程,我们继续来跟踪:

    /** The result to return or exception to throw from get() */
    private Object outcome;
    
    protected void set(V v) {
            if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
                outcome = v;
                U.putOrderedInt(this, STATE, NORMAL); // final state
                finishCompletion();
            }
        }
    

    在set()方法中,设置了输出变量outcome,之后调用了finishCompletion()方法,

    private void finishCompletion() {
            // assert state > COMPLETING;
            for (WaitNode q; (q = waiters) != null;) {
                if (U.compareAndSwapObject(this, WAITERS, q, null)) {
                    for (;;) {
                        Thread t = q.thread;
                        if (t != null) {
                            q.thread = null;
                            LockSupport.unpark(t);
                        }
                        WaitNode next = q.next;
                        if (next == null)
                            break;
                        q.next = null; // unlink to help gc
                        q = next;
                    }
                    break;
                }
            }
    
            done();
    
            callable = null;        // to reduce footprint
        }
    

    这个方法前面的for循环部分无关紧要,不用研究,直接从倒数第二句开始看,先调用done()方法,再把callable变量置为null,减少资源占用。

    这时候你会发现,怎么看了这么多方法,还没有调用到尽头,到底什么时候结束啊。暂时先不要急,跟踪进入done()方法,我们就会惊喜地发现:

    protected void done() { }
    

    done()方法,里面什么也没有,是需要我们在使用FutureTask类的时候自己来实现的方法!这一连串的调用总算在这里告一段落!高兴过后,再想一想:done()方法出现在这里,意味着什么呢?
    我们回头捋一遍,从进入FutureTask类开始,先找到它的run()方法,查看它是怎么执行的,如果这个任务刚刚被创建,还未执行,那么便启动它,并把结果用set()方法进行设置,最后来到可以自定内容的done()方法。显然,done()方法是在传入的mWork任务执行完毕后调用的方法,我们可以在里面写一些任务一旦执行完毕后需要做的事情,举例的话这里就先不举了。
    现在又有了一个疑问,既然有了set()方法,那一定也有一个与之对应的get()方法。ctrl+F 在FutureTask类里找到这个方法:

    public V get() throws InterruptedException, ExecutionException {
            int s = state;
            if (s <= COMPLETING)
                s = awaitDone(false, 0L);
            return report(s);
        }
    
    private V report(int s) throws ExecutionException {
            Object x = outcome;
            if (s == NORMAL)
                return (V)x;
            if (s >= CANCELLED)
                throw new CancellationException();
            throw new ExecutionException((Throwable)x);
        }
    

    在report()方法里,返回了之前在set()方法中设置过的outcome变量,即返回了任务执行的结果。接下来,ctrl+F搜索整个FutureTask类,却发现,并没有找到get()方法被调用的地方。那么它到底是在哪里用到的呢?

    要解决这个问题,就需要返回刚刚的AsyncTask的构造方法中一探究竟了。至此,也标志着FutureTask类的探究可以基本结束。

    public AsyncTask() {
            mWorker = new WorkerRunnable<Params, Result>() {
                public Result call() throws Exception {
                    mTaskInvoked.set(true);
    
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    Result result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                    return postResult(result);
                }
            };
    
            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 occurred while executing doInBackground()",
                                e.getCause());
                    } catch (CancellationException e) {
                        postResultIfNotInvoked(null);
                    }
                }
            };
        }
    
    public final Result get() throws InterruptedException, ExecutionException {
            return mFuture.get();
        }
    

    显而易见,在done()方法的第二行,就调用了mFuture的get()方法。

    真是得来全不费工夫。经过之前的探索,你应该也知道下一步是做什么了,没错,进入postResultNotInvoked()方法,来看看这里面做了些什么~

    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
    
    private void postResultIfNotInvoked(Result result) {
            final boolean wasTaskInvoked = mTaskInvoked.get();
            if (!wasTaskInvoked) {
                postResult(result);
            }
        }
    
    /* TIP: AtomicBoolea是JAVA中的一个原子变量,
      在这个Boolean值的变化的时候不允许被其他线程打断,保持操作的原子性。*/
    

    有没有发现一个很奇怪的问题?如果wasTaskInvoked的值是false,才会执行postResult()方法,否则将什么也不做。可是在call()方法中的第一句:

    mTaskInvoked.set(true);
    

    就已经设置它为true,并且在AsyncTask类中的其他地方并没有再更改这个设置。也就说明它将一直为ture,那么,postResult()方法将一直不会被执行。
    由此我们可以认为,在done()方法里调用的postResultIfNotInvoked(get())只是一个对任务执行出错与否的检验,真正的任务执行结束之后返回结果的语句并不在这里。
    那么这个结果,究竟是在哪里被返回了呢?
    继续研究call()方法,里面的最后一句代码揭开了这个谜底:

     return postResult(result);
    

    终于来了——postResult()。

    不过先不急着跟进去看我们先回忆一下整个call()方法的调用经过
    在FutureTask类的run()方法中,先把mWorker赋给里面的变量c,再通过调用c.call()执行耗时操作,执行结果赋给变量result。之后,用set(result)方法设置了输出值outcome。
    回到AsyncTask类里,mFuture的get()方法能够获得这个输出值,可是在done()方法中,如果程序正常运行,我们并没有机会用到它。
    所以,唯一能正常拿到这个执行结果result的地方,就是call()执行结束后返回的postResult(result)方法。

    好啦,到这里逻辑依旧很清晰我们可以继续跟进去看一看了

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

    在这个方法里,除了返回结果,还有一些操作:message被用handler组装了一下,其中包括一个奇怪的常量MESSAGE_POST_RESULT,和源码自定义的AsyncTaskResult类的匿名对象。最后被发送了出去。
    要想搞清楚这个消息的意义,还需要看一下getHandler()方法的具体内容:

    private static InternalHandler sHandler;
    
    private static Handler getHandler() {
            synchronized (AsyncTask.class) {
                if (sHandler == null) {
                    sHandler = new InternalHandler();
                }
                return sHandler;
            }
        }
    
    private static class InternalHandler extends Handler {
            public InternalHandler() {
                super(Looper.getMainLooper());
            }
    
            @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;
                }
            }
        }
    

    经过一系列的追踪,来到了InternalHandler类的handleMessage方法,看到了之前那个奇怪的MESSAGE_POST_RESULT的使用含义。
    话不多说,来看switch语句的第一个case吧~不出意料,这里又来了一个调用——result.mTask.finish(result.mData[0])。(为了不耽误程序运行流程,第二个case在后面再进行介绍)
    从result开始看起吧~这个对象所属的类是在AsyncTask类里自定义的一个内部类,具体实现如下:

     @SuppressWarnings({"RawUseOfParameterizedType"})
        private static class AsyncTaskResult<Data> {
            final AsyncTask mTask;
            final Data[] mData;
    
            AsyncTaskResult(AsyncTask task, Data... data) {
                mTask = task;
                mData = data;
            }
        }
    

    可以看到mTask是该类里的一个AsyncTask类型的成员变量,那么,result.mTask.finish(result.mData)不正是意味着调用了AsyncTask的方法嘛?

    接下来快速找到finish()方法,一鼓作气,我们速战速决,看看它都搞了些什么事情:

    private void finish(Result result) {
            if (isCancelled()) {
                onCancelled(result);
            } else {
                onPostExecute(result);
            }
            mStatus = Status.FINISHED;
        }
    
    /* TIP:isCancelled()的意义是,当任务在正常结束之前取消,该方法返回true,否则返回false。
      这就表明,如果任务正常结束了,isCancelled()返回值为false,程序跳到else语句的内容* /
    

    在finish()方法中,如果任务正常结束,便调用onPostExecute()方法,回到UI线程(主线程),将后台(子线程)的计算结果在UI中显示。
    不过这时候就有个问题了,目前我们所做的一系列操作都是在子线程中进行的,那么是怎么切换到主线程来更新UI的呢?
    答案就在之前带领我们一步步查看到这里的postResult()方法中:

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

    简单来说,是这里面用到的message.sendToTarget()方法,把刚刚用handler组装好的消息添加到了后台的消息队列里,主线程会不停地读取消息队列来作出更新操作,当读取到这条消息之后,就可以更新UI了。这样就实现了子线程和主线程之间的通信。

    最后,修改状态标志位mStatus为FINISHED。
    还记得在文章开始,最先看到的executeOnExecutor()方法吗?

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

    当状态标志位修改为FINISHED后,该任务便不能被重新执行了,否则会报错。我们兴奋地发现,到这一步,已经与原点衔接上了!

    不过还有一点没有介绍,就是之前提到过的handleMessage()方法的第二个case:MESSAGE_POST_PROGRESS。

    @WorkerThread
        protected final void publishProgress(Progress... values) {
            if (!isCancelled()) {
                getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                        new AsyncTaskResult<Progress>(this, values)).sendToTarget();
            }
        }
    

    通过在doInBackground()方法中调用publishProgress(),会向消息队列发送这一类型的消息,主线程通过读取消息队列获取消息,来更新进度条的进度数字。至于这其中的逻辑——还记得之前handleMessage()方法中的那第二个case嘛?

    case MESSAGE_POST_PROGRESS:
                        result.mTask.onProgressUpdate(result.mData);
                        break;
    
    @MainThread
        protected void onProgressUpdate(Progress... values) {
        }
    

    在publishProgress()发送消息后,会在第二个case中调用onProgressUpdate()方法,它的具体内容需要我们自己来写,这个方法可以实现在主线程更新进度条UI,大家也都用得很多。

    至此,AsyncTask的4个重要的方法:

    1. onPreExecute()
    2. doInBackground(Params...)
    3. onProgressUpdate(Progress...)
    4. onPostExecute(Result)

    也在分析中一个一个地见到并解释完了。
    文章到这里就要结束了,不知道大家对是不是对AsyncTask内部的机制有一个更清晰的认识和了解了呢~

    最后,作者尚在学习,欢迎指摘~

    相关文章

      网友评论

        本文标题:AsyncTask源码解析o=w=o

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