AsyncTask源码解析o=w=o

今天学习了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 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 implements Callable {
        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 mWorker;
private final FutureTask mFuture

public AsyncTask() {
        mWorker = new WorkerRunnable() {
            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(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 callable;

public FutureTask(Callable 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 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() {
            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(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(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 {
        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(this, result));
        message.sendToTarget();
        return result;
    }

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

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

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

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