AsyncTask源码解析

作者: 求闲居士 | 来源:发表于2018-01-31 15:52 被阅读15次

    一,前言

    今日在使用AsyncTask时,发现对其实现原理又不太记得了,在此再解读一边,以便记忆和使用。

    android3.0前的多线程并行执行的版本年代久远就不再分析了,

    二,使用

    1.场景

    在使用一个Thread处理事务,然后使用Handle在UI线程接收处理结果进行后续工作。简单来说就是替代Thread + Handler 的一种方式。

    这是我第一次使用AsyncTask的状态,用以实现新线程后台操作和UI更新。如网络请求和数据库操作等都会用到AsyncTask。

    但AsyncTask的后台操作耗时也不应该太长,耗时操作还是要使用并发库中的工具类。

    2.参数和方法
    2.1 参数
    private class Test extends AsyncTask<Params, Progress, Result> { ... }
    

    我们继承AsyncTask时,会有三个参数Params, Progress, Result要定义。

    • Params:AsyncTask执行时的输入到doInBackground参数的类型;
    • Progress: 后台执行任务时,计算出的完成进度onProgressUpdate的数值类型;
    • Result: AsyncTask的doInBackground执行完毕后onPostExecute的返回类型
    2.2 方法
    • onPreExecute:在执行后台任务前,会先调用这个方法用于准备工作。
    • doInBackground:在执行完onPreExecute后,将会执行doInBackground接口来完成后台耗时工作execute参数的值将会传入这里成为它的参数。在doInBackground中可以调用publishProgress接口更新进度条,
      进度条的数值将通过onProgressUpdate接口更新到UI界面。doInBackground的返回结果将交予onPostExecute在UI线程处理。
    • onPostExecute:方法参数为doInBackground的返回值,在UI线程中执行。
    • onCancelled:和onPostExecute一样,在doInBackground执行完之后在UI线程中执行。但是在执行cancel后才会调用,此时不会再调用onPostExecute方法了。为了尽快响应cancel,doInBackground中应检测isCancelled的返回值。
    2.3 注意事项
    • AsyncTask的创建和使用必须在主线程中。
    • 每个AsyncTask对象都只能执行一次。
    • 为了线程安全,doInBackground使用的成员变量必须在初始化在构造函数或onPreExecute中,在doInBackground中初始化的的变量,由onProgressUpdate和onPostExecute使用。
    • AsyncTask默认在一个线程中按插入顺序执行,要多线程并发要调用executeOnExecutor方法传入线程。

    三.解析

    解析方式,就从execute方法一步一步走下去。

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

    executeOnExecutor则是指定线程执行的方法,可以看出execute是使用默认线程sDefaultExecutor执行后台任务。

    3.1 sDefaultExecutor

    下面来看看sDefaultExecutor是怎样的,它从表面上看是默认的Executor

    //静态成员变量,类共享
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    //静态内部类,类共享为顺序执行提供便利
    private static class SerialExecutor implements Executor {
            //mTasks用于保存任务,没执行一次execute插入一条,scheduleNext去除一条
            final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
            //正在执行的任务
            Runnable mActive;
    
            public synchronized void execute(final Runnable r) {
                //插入任务,被插入的任务将会迭代执行。
                mTasks.offer(new Runnable() {
                    public void run() {
                        try {//先执行Runnable
                            r.run();
                        } finally { //再条用scheduleNext执行下一个任务。
                            scheduleNext(); 
                        }
                    }
                });
                //如果当前没有正在执行的任务,调用scheduleNext取出任务执行
                if (mActive == null) {
                    scheduleNext();
                }
            }
            
            //如果队列有任务,则取交予THREAD_POOL_EXECUTOR执行
            protected synchronized void scheduleNext() {
                if ((mActive = mTasks.poll()) != null) {
                    THREAD_POOL_EXECUTOR.execute(mActive);
                }
            }
        }
    

    对ArrayDeque任务队列的取出,是先进先出的策略,每执行一次execute就是插入mTasks队列一个迭代任务,然后判断当前有无正在执行的任务,确保顺序执行

    SerialExecutor是实现顺序执行功能的,真正执行任务的是THREAD_POOL_EXECUTOR线程池。

    THREAD_POOL_EXECUTOR的参数就不再分析了,因为SerialExecutor使其相当于单线程在使用,线程池的并发也就派不上
    用场了。

    3.2 executeOnExecutor
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
                Params... params) {
            //对象创建时默认为PENDING,限制AsyncTask对象只能执行一次
            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()在此执行
            onPreExecute();
            //将参数传给mWorker.mParams
            mWorker.mParams = params;
            //默认情况下将mFuture交予上面的sDefaultExecutor执行
            exec.execute(mFuture);
    
            return this;
        }
    

    onPreExecute在这里最早被执行了,接着sDefaultExecutor开始执行。

    那mFuture和mWorker又是什么呢?从params上看doInBackground和mWorker有关,从execute上看doInBackground又和mFuture有关。可以猜测doInBackground在mWorker中调用,而mWorker又在mFuture中调用。

    3.3 mFuture和mWorker

    还是从exec.execute(mFuture)开始,execute是需要Runnable对象,Runnable接口只有个run,用来执行业务。

    public void run() {
            ....
            try {
                //Callable就为mWorker对象
                Callable<V> c = callable;
                if (c != null && state == NEW) {
                    V result;
                    boolean ran;
                    try {
                    //执行了mWorker对象的call方法
                        result = c.call();
                        ran = true;
                    } catch (Throwable ex) {
                        result = null;
                        ran = false;
                        setException(ex);
                    }
                    if (ran)
                    //调用了finishCompletion方法
                        set(result);
                }
            } finally {
               ....
            }
        }
    /**
         * Removes and signals all waiting threads, invokes done(), and
         * nulls out callable.
         */
        private void finishCompletion() {
            // assert state > COMPLETING;
            ....
            done();//在新建mFuture,覆盖了这个方法
            //置空mWorker的引用
            callable = null;        // to reduce footprint
        }
    

    在run方法中,执行了callable.call方法,执行成功后又调用了done方法。下面就来介绍call和done方法的实现。

    /**
         * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
         */
        public AsyncTask() {
            mWorker = new WorkerRunnable<Params, Result>() {
                //将会在FutureTask的run方法中执行
                public Result call() throws Exception {
                //设置标志位,设置为true后,mFuture中将不会再次调用postResult
                    mTaskInvoked.set(true);
                     Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //doInBackground在此处被调用
                    return postResult(doInBackground(mParams));
                }
            };
            //在创建对象时,传入mWorker作为callable参数,在run中被调用
            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);
                    }
                }
            };
        }
        //当mWorker的call方法被调用后,postResult将不会被调用
        private void postResultIfNotInvoked(Result result) {
            final boolean wasTaskInvoked = mTaskInvoked.get();
            if (!wasTaskInvoked) {
                postResult(result);
            }
        }
    

    由上可知执行顺序:FutureTask.run -> WorkerRunnable.call -> doInBackground -> FutureTask.done。

    对于doInBackground的返回结果,则是由postResult处理。

    private Result postResult(Result result) {
            @SuppressWarnings("unchecked")
            Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                    new AsyncTaskResult<Result>(this, result));
            message.sendToTarget();
            return result;
        }
    
    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;
                }
            }
        }
    //处理返回结果
    private void finish(Result result) {
            //如果调用了cancel,则接下来调用onCancelled,否则onPostExecute
            if (isCancelled()) {
                onCancelled(result);//调用了onCancelled方法
            } else {
                onPostExecute(result); //onPostExecute在此处调用
            }
            mStatus = Status.FINISHED;//修改标识
        }
    

    处理结果通过InternalHandler,最后调用了finish方法来处理,而finish方法根据是否调用cancel来决定调用onCancelled还是onPostExecute。

    这里封装了一个AsyncTaskResult类来传递结果,原因很简单,handler是静态对象,没法直接拿到当前MyAsyncTask的引用。而我们要把task和result对象同时丢给handler,所以要进行一下封装。

    3.4 onProgressUpdate

    在上面的InternalHandler中,当接收消息为MESSAGE_POST_PROGRESS时,将会调用onProgressUpdate来更新进度。

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

    而这个方法是由publishProgress来发送的。所以在doInBackground中,需要手动调用publishProgress方法来更新进度。

    3.5 cancel

    前面说到cancel,会影响onCancelled和onPostExecute的调用。它还会影响FutureTask执行的任务。

    public final boolean cancel(boolean mayInterruptIfRunning) {
            mCancelled.set(true);
            //FutureTask将会执行cancel,根据mayInterruptIfRunning来决定是否立即停止这个任务
            return mFuture.cancel(mayInterruptIfRunning);
        }
    //FutureTask
    public boolean cancel(boolean mayInterruptIfRunning) {
            ....
            try {    // in case call to interrupt throws exception
                //如果为false,则不会停止正在执行的任务
                if (mayInterruptIfRunning) {
                    try {
                        Thread t = runner;
                        if (t != null)
                            t.interrupt();//停止正在执行的任务
                    } finally { // final state
                        UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                    }
                }
            } finally {
                //回调FutureTask的done方法
                finishCompletion();
            }
            return true;
        }
    

    mayInterruptIfRunning为true,FutureTask会告诉线程停止执行这个任务,否则不会停止线程继续执行。

    随后执行finishCompletion,而finishCompletion方法中会调用done方法,再清空callable也就是WorkerRunnable对象。

    上面分析mWorker的代码中讲过,mFuture.done中执行的postResult,是由mWorker的call方法是否执行决定决定的。如果任务正在执行,则由WorkerRunnable的call方法发送消息执行onCancelled,否则由FutureTask的done发动消息执行onCancelled。

    也就是说,mFuture.done方法发送消息,只能是执行cancel且要取消的任务还在队列中。

    四 总结

    总结下普通的一个执行流程:execute(Params... params) -> executeOnExecutor(sDefaultExecutor, params) -> onPreExecute() -> sDefaultExecutor.execute(mFuture) -> scheduleNext() -> THREAD_POOL_EXECUTOR.execute(mActive) -> FutureTask.run() -> WorkerRunnable.call() -> postResult(doInBackground(mParams)) -> result.mTask.finish(result.mData[0]) -> onPostExecute(result)

    我做了个简单的时序图:https://www.processon.com/view/link/5a717790e4b0874437b31e97

    AsyncTask.png

    其中在FutureTask中
    FutureTask.run() -> WorkerRunnable.call() ->
    FutureTask.set(V v) -> FutureTask.finishCompletion() -> FutureTask.done() -> AsyncTask.postResultIfNotInvoked(Result result) -> postResult(Result result) -> onCancelled(result);

    用流程图或UML画图更直观展示,以后有机会再补充。

    五 参考

    java 内部类和静态内部类的区别

    android多线程-AsyncTask之工作原理深入解析(下)

    Android 7.0 AsyncTask分析

    相关文章

      网友评论

        本文标题:AsyncTask源码解析

        本文链接:https://www.haomeiwen.com/subject/cuqlzxtx.html