Android开发源码阅读--AsyncTask

题外篇:最近开始看各种面试题,你懂的,然后发现看的东西老是记不住,于是去技术群里发了牢骚,然后行业大佬说:对着源码,自己撸一篇文章。当时我的心里是拒绝的,因为懒啊,也因为贪,认为只看不写,能看的更多些,但记不住也是没用,就听大佬的,撸一篇试试,这是写文章的原由,好,闲言少叙,切入正题。

顺藤摸瓜篇:打开Android studio,默认打开的是我写练习的一个service类,这不重要,找个空的地方反手就是一个New,约会的方式有很多种,但这不重要,重要的是我们约到了AsyncTask(API26)

AsyncTask mAsync = new AsyncTask() {
    @Override
    protected Object doInBackground(Object[] objects) {
        return null;
    }
};

接下来我又随便找了个方法地方搞了这个

Android开发源码阅读--AsyncTask

看到execute点进去看看咯

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

execute调用了executeOnExecutor,从方法的结构上可以看出,execute中并没有开启新的线程,所以它还是运行在当前线程的,当然execute这个方法还有个重载,那个并不是我们想要的,忽略了,在往下走之前,我们先看看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)
 */
看到这些,我是头大点,但是踏下心来还是能看懂的,看我的神翻译,前两行大概说,带参数执行,返回自己以便调用者持有任务的引用(返回自身引用的点好处还可以写链式代码,一直点点点直到结束的那种)。然后接着看下面有Note:介绍,解释,说这个asynctask是吧任务放到一个队列里面,在后台由单独的线程或者有线程池来运行它,线程还是线程池要看版本,什么版本做的变化等等。不做多的翻译了,主要想表达的就是这个解释能帮助我们理解源码,对我这样比较菜的是非常有帮助的,大佬直接能看懂的请忽略

接着往下走,来到executeOnExecutor(sDefaultExecutor, params)方法内

@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;
}
我们可以看到前边先是有个运行状态的判断,PENDING是还没运行,RUNNING是正在运行,FINUSH运行完成,状态判断完后会将当前的状态只为运行状态,之后运行的是onPreExecute();这个方法,我们常重写的方法一个运行的已经出现了。下面是mWorker,它是asyncTask的一个静态内部类,mParams是静态内部类的成员变量,mWorker实现了Callable<T>接口,Callable不是我们的重点,你可以理解为有返回值的Runnable,那这行代码的意图就是把调用的传进来的参数保存到mWorker的成员变量中,继续向下,exec,这个是从execute传进来的线程池,一直追溯可得这个线城池是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);
        }
    }
}
它的内部有一个任务队列,串行执行,它是线程安全的,它主要负责将任务队列中的任务分发给执行线程池在执行,这个执行线程是在静态代码块中初始化的;以上是任务的分配,并且我们找到了AsyncTask第一个执行的方法onPreExecute(),任务分配完以后,我们看看任务里面都做了什么,我们追溯mActive->mtasks->mfuture->mworker
mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);
        Result result = null;
        try {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            result = doInBackground(mParams);
            Binder.flushPendingCommands();
        } catch (Throwable tr) {
            mCancelled.set(true);
            throw tr;
        } finally {
            postResult(result);
        }
        return result;
    }
};
这里重写了call方法,相当于ruannable的run,在这个方法里我们看到了熟悉的doInBackground(mParams,mParams是作为worker的成员变量,已经在前边executeOnExecutor赋值,这里传入doInBackground(mParams)这个方法里面,便于我们在重写doInBackground(mParams的时候使用,这样,我们在execute里面穿的params就传到了doInBackground里面了,接着doInBackground将结果返回给result,之后result在finally中的postResult(result)中被hander机制用message传回主线程,什么?hander也要说下?那就点进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;
}
其中handler最终是用getMainHandler或者是传入mainLooper得到的主线程的handler,这次我们主要看AsyncTask哈,就不细说了,接下来看看发送的消息里面执行了啥,

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;

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}
在来看看更新进度
@WorkerThread
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}
可以看出更新进度主要是往主线程发消息,传的参数就是我们想要的进度,正好DoInBackground里面有结果,在里面调用publishProgress,然后主线程里面的OnProgressUpdated就会被调用,在里面更新UI吧
总结篇:嗯这里应该有一个流程关系图,但是楼主懒癌犯了Android开发源码阅读--AsyncTaskAndroid开发源码阅读--AsyncTaskAndroid开发源码阅读--AsyncTask