AsyncTask从精通到放弃(二)

时光荏苒,距离我上次写AsyncTask的文章已经过去快一年了

AsyncTask从精通到放弃(一)

那个时候的我只get到了它的使用规律但是没有研究过源码,最近在强化自己对于源码的理解能力,所以对其源码进行了研究,如果对AsyncTask不熟悉的朋友可以看完我上面的博客再看这篇文章

我们一般使用AsyncTask的姿势如下:

 new AsyncTask<String, Void, String>() {

            @Override
            protected String doInBackground(String... strings) {
                return null;
                //后台操作
            }

            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
                //更新UI
            }
        }.execute("");

这两个回调方法是我们最熟悉的,或许大家还能记得
onPreExecute
onProgressUpdate
onCancelled
即使忘了也没关系,接下来我们分析源码的时候会带领大家重新认识这些方法

在开始這篇博客之前,需要提出几个问题?

1 AsyncTask实现原理是什么?

2 为什么AsyncTask只能在主线程创建(new AsyncTask)?

3 为什么说AsyncTask是串行执行?

接下来让我们看源码来分析这几个问题

首先是对象的创建(new AsyncTask),我们常用姿势就是创建一个匿名内部类(常常是无参的)

public AsyncTask() {
        this((Looper) null);
    }

这是一个重载方法,具体的实现在这里

-----code 1----
public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

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

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

因为这段代码很重要所以我给他命名为”code 1”,之后还会再看这段代码,这里我们只需要了解无参构造方法传null,所以mHandler将会走getMainHandler()方法

private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

InternalHandler是Handler的子类,这里面声明了一个InternalHandler类,sHandler是AsyncTask类的静态成员,所以AsyncTask在哪个线程创建,sHandler就属于哪个线程,因为方法参数已经规定是Looper.getMainLooper(),所以AsyncTask对象的创建必须在主线程,关于这个sHandler有什么用,我们等会再讲,先在这里埋下伏笔

接着源码执行的入口是execute方法

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

我们看到这里调用了executeOnExecutor方法并且传递了sDefaultExecutor参数,让我们看下sDefaultExecutor的样子

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

这个sDefaultExecutor是一个静态常量SerialExecutor,接下来看executeOnExecutor执行了什么

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

每个AsyncTask都有一个执行的状态(即将开始,已经开始,已经结束),只有即将开始的任务才可以被执行,其余的都不可以,会抛出异常(这也是为什么AsyncTask只能调用execute一次的原因),这个时候我们看到了 onPreExecute()方法,没错!这个时候还没有启动线程所以可以用来做热身工作,紧接着调用了exec.execute(mFuture)也就是我们上面提到的SerialExecutor,它调用内部的execute方法,这个时候我们好奇的是这个mFuture是从哪里来的?

让我们把目光回到code 1,mFuture就是一个FutureTask,这个FutureTask的参数是mWorker,等FutureTask执行run方法就会回调mWorker的call方法,代码如下

//FutureTask类
public void run() {
   result = c.call();         
}
//WorkerRunnable
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;
            }
        };

这里出现了doInBackground方法,为我们在后台执行了耗时操作,执行完毕之后postResult方法把结果发送出来,因为这里的线程已经不是UI线程了,所以不能直接操作result,需要通过post传递出来,看到post你想到了什么?Handler!!没错!

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

使用了Message传递消息,既然有人传递就有人接受消息

private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

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

果然我们前面提到的sHandler在这里派上了用场,用于收发消息,与此同时我们还能看到onProgressUpdate方法,这个就是我们开头提到的onProgressUpdate方法,既然拿到了结果走到了finish方法,那么看下finish方法有什么猫腻?

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

真相大白,原来onCancelled和onPostExecute都是在这里进行,二者只能取其一,所以如果一个任务被onCancelled了就不能走onPostExecute了

上述分析解释了整个异步任务的流程,但是有个问题一直悬而未决

FutureTask只是实现了Runnable接口,但是在哪里开启线程执行FutureTask()的?

刚才我们分析到SerialExecutor,知道是调用它的execute方法开启了线程

  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串行执行的原理
ArrayDeque是一个双端队列,就是只能在头和尾进行插入删除操作,offer就是队尾插入一个元素,poll就是获取队头的元素并且删除这一个元素。因为SerialExecutor是静态类,所以mTasks和mActive是所有AsyncTask对象共享(所以方法前需要加synchronized,防止数据多线程操作之后变脏)。

无论多少个AsyncTask被执行但是都会按照先后顺序从队尾添加到ArrayDeque,起初mActive为空,所以直接进入scheduleNext方法,这个方法是执行队列的头元素(执行头元素的run方法,再执行scheduleNext方法如此循环)直到所有的元素都执行完保证所有的run方法按照顺序执行

如果第一遍咩有理解这句话,希望大家多读几遍,多理解一下,下面配张图希望能帮助大家理解下

这里写图片描述

先执行1,再执行2,2中执行3中头元素的run方法再继续执行2方法,如此循环直到队列中不存在头元素,这就是AsyncTask的串行执行原理

你可能现在还有疑问,我还是没有看到任何一个Thread啊!难道不是用Thread.start?

这个时候要说说我们本场的重量级嘉宾THREAD_POOL_EXECUTOR

  public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

没错,虽然你不一定知道这是什么东西,但是这个ThreadPoolExecutor你一定见过,足以可见其牛逼程度了,这是一个线程池调度器,几乎所有的线程相关都有他,在这里不深入讲,只知道他的作用即可,后面写博客的时候会详细讲ThreadPoolExecutor的源码

维持有限数量的线程池,通过复用线程提高效率

public void execute(Runnable command) {
       //其他省略
       addWorker(command, true)
 }
private boolean addWorker(Runnable firstTask, boolean core) {
     //省略
     final Thread t = w.thread;
       if (workerAdded) {
           t.start();
           workerStarted = true;
        }
}

至此,AsynTask就全部分析完毕了,感谢各位朋友能坚持读到现在,如果有什么不懂得地方欢迎指出,希望一起进步,一起努力~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值