AsyncTask 第三篇源代码篇

转载注明出处:http://www.jianshu.com/p/26e1f7be4d0d

简介

上一篇主要针对AsyncTask内部使用到的一些线程的技术进行了基本的讲解,如果还没有看过的同学,可以点开这个AsyncTask 第二篇线程篇去查看一下。这一篇就从源代码分析AsyncTask的具体实现,也终于从第一篇的使用到了实现的思路。下面就直接开始了。

方法调度说明

AsyncTask里面其实我们最关心里面实现的思路了,这就需要结合一下AsyncTask怎么使用,才更方便的理解内部的实现,如果还有同学并不了解它的使用,可以查看AsyncTask 第一篇使用篇这篇文章。

所有的开始都是从调用AsyncTask.execute(params)方法,我们直接看一下这个方法吧。

public final AsyncTask execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

execute(params)内部直接调用了executeOnExecutor方法,并将一个Executor的实例化对象传入,同时也将运行参数传入,我们来看一下executorOnExecutor方法吧。

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

内部居然就有了详细的实现,也有我们非常熟悉的一个方法onPreExecute(),可以看出来,在我们调用了AsyncTask.execute(params)方法后,紧接着就会调用onPreExecute()方法,这个方法我们可以复写,并做一些任务执行前的一些操作。

在这个方法里面,首先会对自身状态进行判断,如果是RUNNINGFINISHED状态就会抛出异常,也就是前面提示的每一个实例化的AsyncTask只能执行一次,如果执行多次会抛出异常。判断完毕后,将自身状态设置为RUNNING状态,并调用onPreExecute()方法,接着将参数传入mWorker中,执行mFuture,那说明mFuture即可以作为线程运行的结果,也可以作为一个线程执行的任务,根据上一篇关于线程的文章,我们就可以大胆猜测,mWorker实现了Callable接口,而mFuture其实是一个FutureTask的实例化对象。

赶紧找到mWorker的声明。

private final WorkerRunnable mWorker;

......

private static abstract class WorkerRunnable implements Callable {
    Params[] mParams;
}

找到声明处,紧接着找到了具体的类,果然和我们猜测一样,mWorker实现了Callable接口,但是只是一个抽象类,里面声明了一个泛型的成员变量,没有call()具体实现,我们直接去找mWorker实例化的地方,看一下call()方法的具体实现。

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

我们可以看见在AsyncTask构造函数里面就会对mWorkermFuture实例化,在mWorker对象中的call()方法里面,先设置了线程优先级,然后调用了我们熟悉的方法doInBackground(),并获取了运行的结果,调用了postResult()方法将结果传入。mFuture实例化的类获取了mWorkder实例化对象,并复写了done()这个方法,很容易理解这个done()方法是线程具体任务执行完毕后调用的,复写done()方法主要是保证获取到执行结果,我们看一下postResultIfNotInvoked()这个方法就明白了。

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        postResult(result);
    }
}

在这个方法里面,也会将执行结果传入postResult()方法。

在这里我们看见了两个比较熟悉的方法,一个是doInBackground()一个是postResult(),我们先来看doInBackground()吧。

protected abstract Result doInBackground(Params... params);

是一个抽象方法,也就是说我们在继承或者直接实例化实时必须实现她。

再来看一下postResult()

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

将执行结果通过Handler传入了UI线程中的某个方法,想必大家都猜到了最终会调用到onPostExecute(Result result),但是目前为止我们只是获得了运行的结果,但是并没有获取到在执行过程中的执行状态。去看一看publishProgress一探究竟。

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

原来这个方法也是将用户传入的执行状态直接通过Handler传到UI线程的某个方法,对,就是onProgressUpdate方法。可以看见最关键的两个异步消息的处理,结果和执行状态信息都是通过Handler传到UI线程,直接看一下Handler的声明。

private static class InternalHandler extends Handler {
    public InternalHandler() {
        super(Looper.getMainLooper());
    }
    @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;
        }
    }
}

Handler里面就处理了两类消息,一个是抛出运行结果消息,一个是抛出运行状态的消息。如果是运行状态,直接回调用AsyncTaskonProgressUpdate方法,和大家猜想是一样的。如果是运行结果,会调用finish方法,我们看看这个方法,为什么不是直接调用onPostExecute方法。

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

很明显,如果在执行过程中,用户没有去取消,就会调用onPostExecute方法,如果取消了就会调用onCancelled方法。然后最终,会将AsyncTask的执行状态变为FINISHED状态,一旦置为这个状态,再去调用AsyncTask.execute(params)就会抛出异常,这个在调用该方法时候就会去判断,大家可以去上面看一下这块源代码。

到目前为止,AsyncTask内部方法间的调度顺序大家通过源代码想必有了大体的认识。但是如果现在让大家自己写一个类似的类DemoTask,大家会怎么实现?根据上一篇的文章,我们会直接实例化一个Executor去执行FutureTask,并使用Handler来处理异步线程间通信。这样是正确的做法,但是一旦当我们自定义的DemoTask被大量时候的时候,我们并不能对它有一个统一的管理,所以我们有必要看一下官方对于这一块是怎么处理的。

线程调度说明

大家记得在调用AsyncTask.execute()方法时候,会有一个sDefaultExecutor实例化对象出现过吧,很明显,它是用来执行FutureTask的,而且是个常量,直接去看一下它的定义。

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

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

代码很容易理解,它在执行时候会将FutureTask放入一个匿名Runnable的执行方法中,然后将这个匿名Runnable添加到自身一个成员变量,如果首次调用这个方法,会执行调用scheduleNext方法,使用线程池去运行这个匿名的Runnable。如果当前匿名Runnable中的FutureTask执行完毕,也会调用ScheduleNext方法。

我们来看一下线程池THREAD_POOL_EXECUTOR声明的地方。

public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, 
                                MAXIMUM_POOL_SIZE,                              KEEP_ALIVE,
                                TimeUnit.SECONDS, 
                                sPoolWorkQueue, 
                                sThreadFactory);

前面这些常量通过字面含义就能大体了解含义了,如果有同学想知道具体含义,可以去查看一下源代码,我们现在仅看一下sPoolWorkQueue这个参数。

private static final BlockingQueue sPoolWorkQueue =
        new LinkedBlockingQueue(128);

很明显,它是一个用来存储线程池中线程的队列,而且最多能容纳128个线程。而且由于这个线程池是全局的一个静态常量,所以在一个进程中所有我们实例化AsyncTask的具体执行任务都会被添加到这个线程池中。

至此关于AsyncTask中线程的调度,也有了一个清晰的脉络。

总结

虽然AsyncTask是一个快过时的类,而且它不适合执行长时间后台操作,但是在许多场景我们依然会需要到它,而且通过源代码,我们可以对这种作者的思路做出一个很好的总结,对Handler的使用场景和使用方法,有了更深的认识。至此,我们也可以参考AsyncTask的思路,自定制适合自己的异步消息处理的机制。

你可能感兴趣的:(AsyncTask 第三篇源代码篇)