AsyncTask源码分析

在Android中,如果需要执行一个较短的耗时任务(几秒钟内),比如图片下载,数据库访问等,我们通常会使用AsyncTask来执行。如果你对AsyncTask如何使用不是特别了解,可以看看AsyncTask简单实例
使用AsyncTask来处理异步任务给大家带来了很多便利,你不需要操作Thread和Handler,就能很方便的在工作线程和UI线程中做自己想要做的事情。那么AsyncTask是如何做到的呢?本文就以Android 7.0这个版本的源码来分析一下AsyncTask的工作原理。

在分析代码之前,我们先回想一下我们一般是如何使用AsyncTask的。一般步骤如下:

  1. 继承AsyncTask实现它的子类,然后生成一个AsyncTask对象
  2. 在UI线程执行execute()
  3. 在UI线程onPreExecute()中做一些初始化操作,比如说显示进度条什么的
  4. 在工作线程中调用doInBackground()执行异步耗时任务,如果需要获取进度信息,那么在doInBackground()中调用publishProgress(),此时onProgressUpdate()就会被回调
  5. 在UI线程中调用onPostExecute()进行后处理,比如下载图片后将它显示在ImageView上
  6. 可以调用cancel来取消任务,如果取消成功,则会调用onCancelled()
    现在我们就以这个顺序来看看源码实现。

1.AsyncTask的任务生成

 public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
              //这里细节先不看
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
               //这里细节先不看
            }
        };
    }

这里主要初始化了两个变量,一个是WorkerRunnable变量,另一个是FutureTask变量。其中FutureTask是Future接口的一个默认实现,通过它可以执行任务,并且可以查看任务执行结果等。如果不是很清楚的话,可以看看Runnable,Callable,Future和FutureTask简介。那么什么是WorkerRunnable呢?我们先看看定义。

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

从这里我们可以看出它是一个抽象类,跟Callable接口的区别就是多了一个Params[]成员变量。

2. AsyncTask的任务执行

@MainThread//注解表明需要在UI线程执行这个函数
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);//sDefaultExecutor等一会看它的实现
    }

很简单,直接调用executeOnExecutor(sDefaultExecutor, params)函数,那么我们进入这个函数看看。

 @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {//初始化AsyncTask时状态为PENDING,执行过程中为RUNNING,结束任务为FINISHED。
            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();//注意看这里,好熟悉的身影,对了,就是在这里做一些初始化动作,比如显示进度条等,很显然,也需要运行在UI线程
        mWorker.mParams = params;//这里,把运行这个任务的参数赋值给了mWorker,那么mWorker是什么,如果你忘了,那还是去看看构造函数。。。,它就是个Callable对象,只不过多了个mParams成员而已。
        exec.execute(mFuture);//exec就是sDefaultExecutor, mFuture就是个FutureTask,

        return this;
    }

从上面的代码看出,一个AsyncTask变量(代表一个任务)只能执行一次,因为如果这个任务在执行中或者执行结束,那么再运行这个任务,就会抛出异常了。

我们继续往下看exec.execute(mFuture)。mFuture先不管,就把它当做一个任务好了。先看看exec,也就是sDefaultExecutor的实现。找啊找。。。嘿呦嘿呦,找到了,定义如下:

private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();//保存任务的队列哦
        Runnable mActive;//现在运行的任务

        public synchronized void execute(final Runnable r) {//exec.execute(mFuture)调用的就是这个函数啊,不对啊,它是Future,我是Runnable,你确定可以吗?当然是可以的,因为FutureTask也实现了Runnable接口,万能的多态啊,I love it。
            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的初始化部分,在接下来会分析,客官,骚等。。。
            }
        }
    }

上面的这段代码主要实现了任务如何入队,以及什么时候执行的问题。execute(final Runnable r)先将当前的任务放在任务队列中,这个时候有两种情况:一种是第一个任务进来时,先将这个任务入队,由于mActive == null,此时从队列中取出这个任务直接运行。另外一种就是其他任务进来时,由于mActive已经不为空了,那么execute只会做入队操作。那么其他任务什么时候执行呢?看看上面的代码finally部分,它的意思就是说只有一个任务完成以后,下一个任务才会开始执行。目的就是为了实现每个时间点只有一个任务在执行,其他任务只能等待。这也是为什么AsyncTask一直强调比较适合在任务耗时比较小的情况下使用的原因,因为如果这个任务耗时比较长,那么其他任务只能等待。

3.FutureTask的任务实体

前面代码已经看了一大坨了,还是先整理整理思路。主要做了这些事情:

  1. AsyncTask的初始化,主要是初始化了mWorker和mFuture,其中mWorker是Callable对象,mFuture是FutureTask
  2. 然后调用execute(params),将params赋值给了mWorker,并通过一个执行器执行mFuture这个任务
  3. 这个执行器实现了每个时刻只运行一个任务,一个任务完成了再执行下一个任务

通过梳理,我们已经大致知道一个AsyncTask的调度过程,现在来看看一个任务主要做了哪些事情。在AsyncTask里初始化时,我们将相关代码先忽略了,现在因为要看实际的运行任务,是时候把它拿出来看看了。

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);//先注意mParams,这个参数我们在调用execute时候已经给它赋值了。由于整个任务运行在执行器中,所以doInBackground是运行在工作线程中的。
                Binder.flushPendingCommands();
                return postResult(result);//运行结束后将这个结果发送出去。
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {//在这个futureTask运行结束后会回调这个函数
                try {
                    postResultIfNotInvoked(get());//任务结束后,通过future的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);
                }
            }
        };
    }

首先需要明白的是,一个FutureTask的任务实体是mWorker。如果不是很明白,可以去看看Runnable,Callable,Future和FutureTask简介后面的FutureTask的代码分析。在FutureTask的run()函数里会直接取出callable,然后执行callable实体的call()函数。在call()函数中,我们调用了doInBackground(),并且将这个函数的执行结果出去了。至于如何发送的,请看看下文。

4.AsyncTask任务结果显示和进度信息获取

现在我们来看看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,然后生成一个AsyncTaskResult对象,把它发送出去。其中AsyncTaskResult包括了this(AsyncTask)和result(任务执行结果)。由于这个结果是通过Handler发送的,
那我们看看Handler的代码实现,代码如下:

private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());//这是UI线程的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]);//result.mData[0],这个就是任务的执行结果
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);//
                    break;
            }
        }
    }

在这里调用了AsyncTask的finish方法,我们去看看吧。

private void finish(Result result) {
        if (isCancelled()) {//如果被取消了
            onCancelled(result);
        } else {
            onPostExecute(result);//正常情况下
        }
        mStatus = Status.FINISHED;//任务状态置为结束
    }

这段代码信息量蛮大的,我们可以看到onCancelled(result)和onPostExecute(result)回调,这两个回调都在UI线程中执行。如果一个任务被取消了,那么只有onCancelled()会被回调,否则,如果任务顺利完成了,那么只会执行onPostExecute()回调。不管是任务取消了,还是任务完成了,任务状态都会被置为完成。

至此,我们大致已经了解AsyncTask的内部实现。现在还有一个经常会用到的onProgressUpdate()回调接口,我们知道,如果需要获取进度,我们需要在doInBackground()调用publishProgress()函数。

 @WorkerThread//在工作线程中,也就是说需要在doInBackground()中调用
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {//如果没有被取消
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();//生成AsyncTaskResult对象,将进度信息发送给消息队列,然后在handleMessage()里处理。
        }
    }

InternalHandler的实现

 case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);//获取到进度信息
                    break;

5.AsyncTask代码总结

现在我们简单梳理一下AsyncTask的源码:
1. AsyncTask的初始化,主要是初始化了mWorker和mFuture,其中mWorker是任务实体,mFuture可以对任务进行管理,如取消,获取结果等。
2. 然后调用execute(params),将params赋值给了mWorker,并通过一个执行器执行mFuture这个任务
3. 这个执行器实现了每个时刻只运行一个任务,一个任务完成了再执行下一个任务,任务不会并发执行
4. 这个执行器最终执行任务是通过线程池来完成的
5. 在mWorker里,调用了doInBackground,我们可以在这个函数里实现耗时任务。任务执行结束后,调用postResult(result),通过handler将运行结果发送到UI线程
6. 在UI线程根据执行结果来调用onPostExecute()
7. 如果在doInBackground中调用了publishProgress,那么会回调onProgressUpdate
8. 如果你想任务并行执行的话,那么调用executeOnExecutor(Executor exec,
Params… params),其中exec可以自己定义

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值