Android中AsyncTask源码解析

AsyncTask可以实现异步工作,它将耗时操作放在一个工作线程中进行,然后将这个工作线程的结果发送给UI线程来进行UI更新操作,用起来非常的方便。

下面我们先来简单说说AsyncTask的用法吧?

public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {

    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }

    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}

下面简单看看它是如何工作的?

1、当执行了AsyncTask的execute函数,doInBackground()会在一个工作线程中自动的执行。
2、onPreExecute(), onPostExecute(), onProgressUpdate()会在UI线程中执行。
3、doInBackground()函数的返回值会发送到onPostExecute()函数,作为它的参数传递进去。
4、在doInBackground()函数中可以调用publishProgress()函数,它会触发UI线程中的onProgressUpdate()函数执行。

也是就是说,在使用过程中,我们将耗时的操作放在doInBackground()函数中,这个函数会运行在一个单独的工作线程中,这个函数有一个返回值,这个返回值会作为onPostExecute()的参数,这样onPostExecute()函数就可以获得doInBackground()的运行结果,在UI线程中来进行更新的操作,另外,如果我们在doInBackground()中希望实时的进行UI的更新操作,我们可以在doInBackground()函数中调用publishProgress()函数,这个函数会触发UI线程中的onProgressUpdate()的执行。

上面把这个类的用法基本都说明清楚了,下面我们需要通过源码来说说它的工作原理了。

public abstract class AsyncTask<Params, Progress, Result> {

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

    protected void onPreExecute() {
    }

    protected void onPostExecute(Result result) {
    }

    protected void onProgressUpdate(Progress... values) {
    }
}

上面的代码把前面说的几个函数都展示出来了,基本都需要我们自己去实现,把自己的处理逻辑加进去。我们需要说明的一点就是AsyncTask<Params, Progress, Result>这里面的三个参数Params, Progress, Result,从上面的代码中也可以看到Params对应的就是doInBackground参数类型,Progress为onProgressUpdate的参数类型,Result就是doInBackground的返回值类型以及onPostExecute的参数类型。

下面我们来看看AsyncTask的execute方法。

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

上面执行调用了函数executeOnExecutor,它带有两个参数:一个是sDefaultExecutor,它是一个Executor,另一个是params,它是doInBackground函数需要传入的参数,也就是它会使用线程池来实现这个异步操作。

下面看看sDefaultExecutor这个参数的定义:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

public static final Executor SERIAL_EXECUTOR = new 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);
        }
    }
}

上面代码可以看到SerialExecutor的实现,重点是execute方法,当传入一个Runnable,可以理解为它会把Runable存放到了一个ArrayDeque里面,后面就是使用scheduleNext方法来遍历这个ArrayDeque,取出每一个Runnable,添加到一个THREAD_POOL_EXECUTOR线程池中。

我们来看看THREAD_POOL_EXECUTOR这个线程池的创建。

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

这样在线程池中就会调用Runnable的run方法。

接着上面的看,当调用AsyncTask的execute方法,这个方法里面会执行executeOnExecutor函数,我们来看看这个函数。

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是传进来的sDefaultExecutor对象
    //这里实质调用的就是上面SerialExecutor类里面的execute方法
    exec.execute(mFuture);

    return this;
}

看上面的注释,上面调用的是SerialExecutor类里面的execute方法,这个我们在上面分析过了,其实就是将mFuture放到了一个线程池中,我们来看看mFuture是什么东西。

private final FutureTask<Result> mFuture;
private final WorkerRunnable<Params, Result> mWorker;

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);

            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            return postResult(doInBackground(mParams));
        }
    };

    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 occured while executing doInBackground()",
                        e.getCause());
            } catch (CancellationException e) {
                postResultIfNotInvoked(null);
            }
        }
    };
}

从上面代码我们看到mFuture就是一个FutureTask对象,关于FutureTask的用法,可以参考Executor线程池的几种用法这篇文章,FutureTask的创建接收一个Callable对象,上面这个Callable就是WorkerRunnable,我们可以来看看这类。

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

在线程池中最终会执行WorkerRunnable里面的call方法,关于为什么会调用,可以看看Executor线程池的几种用法这篇文章。

下面我们来把思路整理一下:
下面通过我们的使用过程来进行分析。

1、DownloadImageTask task = new DownloadImageTask()
我们使用AsyncTask的时候会首先创建一个AsyncTask对象,在构造函数里面主要做了一件事,那就是创建一个FutureTask对象,FutureTask会接收一个Callable对象,就是WorkerRunnable。

2、task.execute()

它里面会调用executeOnExecutor函数,在executeOnExecutor里面会调用SerialExecutor的execute方法,在这个方法里面会将上面创建的FutureTask对象传递进行,然后它首先会把这个FutureTask封到一个Runnable里面,放到ArrayDeque里面,接着会从ArrayDeque里面一个一个的取出Runnable放到一个线程池中,线程池会分配一个线程最终会执行到FutureTask里面的call方法。

下面我们来看看FutureTask里面的call方法的实现。

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);

        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        //noinspection unchecked
        return postResult(doInBackground(mParams));
    }
};

1、它首先调用的是doInBackground方法,这个方法就是我们要实现的方法,从这里我们可以看到这个doInBackground运行在线程池的一个线程中,也就是一个单独的工作线程中。

2、运行完成后,会调用postResult来处理doInBackground返回的结果。我们来看看它的具体实现。

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

从上面的代码可以看出,它把doInBackground返回的结果封装到一个Message消息中,通过sHandler发送出去。

下面我们来看看sHandler的具体代码。

private static final InternalHandler sHandler = new InternalHandler();

private static class InternalHandler extends 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]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

我们知道这个消息会发生到一个消息队列里面,在UI线程中有一个Looper它会不断的从消息队列获取消息,最终回调InternalHandler的handleMessage方法,也就是说handleMessage方法运行在UI线程,现在已经不是线程池的那个线程了,关于Handler和Looper,可以看看这篇文章Looper与Handler解析

从这里我们看看AsyncTask里面有一个线程池,耗时操作doInBackground会运行在线程池的线程中,UI线程和线程池的线程是通过Handler来进行通信,当耗时操作doInBackground在线程池的线程中运行完成,通过Handler把最终的结果带回到UI线程中,在UI线程中,也就是handleMessage中会拿到这个结果,从上面代码我们可以看到这个结果是被封装过的,它里面包含有AsyncTask的实例对象,最终会运行它的finish方法,我们来看看这个finish方法。

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

从上面我们就看到它最终执行了onPostExecute,也就是说最终带回结果之后,在UI线程中它会执行onPostExecute函数并且把这个结果作为参数传递进去。

从这里我们做几点总结:
1、doInBackground运行在线程池的线程中
2、onPostExecute运行在UI线程中
3、这两个线程是通过Handler来进行通信的

这样就是实现了异步操作了,上面我们提到publishProgress()方法,我们知道这个方法是在doInBackground中也就是线程池线程中运行的,主要是为了跟UI线程进行通信来更新UI,下面我们再来看看这个方法。

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

从上面代码,就可以看出它也是通过Handler来进行通信,它通过handler来通过UI进行更新,从上面的handleMessage里面我们也可以看到它的处理逻辑,它调用了onProgressUpdate函数,这个函数需要由我们自己来实现,也就是说这个函数运行在UI线程。

Executor线程池的几种用法这篇文章中,我们可以知道FutureTask任务是可以被取消的,我们耗时任务是封装成FutureTask放到线程池中运行的,所以我们也可以对其进行取消,这里说明一点,AsyncTask里面的任务是可以取消的。下面我们来看看它的cancel函数。

public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);
    return mFuture.cancel(mayInterruptIfRunning);
}

从这里我们就可以看到它确实调用的是FutureTask的取消来取消这个任务的。

上面的整个过程将的相对比较浅,主要为了说明AsyncTask的本质还是Thread+Handler。另外推荐比较好的两篇文章:

Android AsyncTask完全解析,带你从源码的角度彻底理解

Android源码分析—带你认识不一样的AsyncTask

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值