AsyncTask使用与源码解析(二)

来源:互联网 发布:销售数据统计表模板 编辑:程序博客网 时间:2024/06/13 21:38

本文承接于AsyncTask使用与源码解析

下面我们来看一下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();//scheduleNext,则直接取出任务队列中的队首任务,如果不为null则传入THREAD_POOL_EXECUTOR进行执行。            }        }        protected synchronized void scheduleNext() {            if ((mActive = mTasks.poll()) != null) {                THREAD_POOL_EXECUTOR.execute(mActive);            }        }    }

当有任务到来时, mTasks.offer就把任务入队,然后在scheduleNext中出队一个任务,然后放到THREAD_POOL_EXECUTOR里去执行,虽然是放到THREAD_POOL_EXECUTOR去执行了,可是一次只放一个任务,因此是串行执行的,所以这个SerialExecutor的作用就是将任务排好队,一个一个放入线程池中去执行,所以我们直接调用execute开启一个AsyncTask的时候,任务是串行执行的。

SerialExecutor类中也有一个execute()方法,这个方法里的所有逻辑就是在子线程中执行的了,注意这个方法有一个Runnable参数,这个参数的值是什么呢?是mFuture对象,在r.run();我们要调用的是FutureTask类的run()方法,而在这个方法里又会去调用Sync内部类的innerRun()方法,我们来看innerRun()方法。

SerialExecutor是使用ArrayDeque这个队列来管理Runnable对象的,

如果我们一次性启动了很多个任务,首先在第一次运行execute()方法的时候,会调用ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部,

然后判断mActive对象是不是等于null,第一次运行当然是等于null了,于是会调用scheduleNext()方法。在这个方法中会从队列的头部取值,并赋值给mActive对象,然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。之后如何又有新的任务被执行,同样还会调用offer()方法将传入的Runnable添加到队列的尾部,但是再去给mActive对象做非空检查的时候就会发现mActive对象已经不再是null了,于是就不会再调用scheduleNext()方法。

那么后面添加的任务岂不是永远得不到处理了?

当然不是,看一看offer()方法里传入的Runnable匿名类,这里使用了一个try finally代码块,

并在finally中调用了scheduleNext()方法,保证无论发生什么情况,这个方法都会被调用。也就是说,每次当一个任务执行完毕后,下一个任务才会得到执行,

SerialExecutor模仿的是单一线程池的效果,如果我们快速地启动了很多任务,同一时刻只会有一个线程正在执行,其余的均处于等待状态。

    //看郭林的代码的时候找不到此段void innerRun() {      if (!compareAndSetState(READY, RUNNING))          return;      runner = Thread.currentThread();      if (getState() == RUNNING) { // recheck after setting thread          V result;          try {              result = callable.call();          } catch (Throwable ex) {              setException(ex);              return;          }          set(result);      } else {          releaseShared(0); // cancel      }  }  

result = callable.call();调用了callable的call()方法,那么这个callable对象是什么呢?其实就是在初始化mFuture对象时传入的mWorker对象了,此时调用的call()方法,也就是一开始在AsyncTask的构造函数中指定的,我们来看一下代码:

public Result call() throws Exception {      mTaskInvoked.set(true);      Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);      return postResult(doInBackground(mParams));  }  

在postResult()方法的参数里面,我们找到了doInBackground()方法的调用处,经过了很多周转,目前的代码仍然是运行在子线程当中的,所以这也就是为什么我们可以在doInBackground()方法中去处理耗时的逻辑。接着将doInBackground()方法返回的结果传递给了postResult()方法。

   private Result postResult(Result result) {        @SuppressWarnings("unchecked")        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,                new AsyncTaskResult<Result>(this, result));        message.sendToTarget();        return result;    }    private static Handler getHandler() {        synchronized (AsyncTask.class) {            if (sHandler == null) {                sHandler = new InternalHandler();            }            return sHandler;        }    }

熟悉了异步消息处理机制,这段代码一定非常简单吧。(觉得不简单的,点击进入)这里使用sHandler对象发出了一条消息,消息中携带了MESSAGE_POST_RESULT常量和一个表示任务执行结果的AsyncTaskResult对象。

private static class InternalHandler extends Handler {        public InternalHandler() {            super(Looper.getMainLooper());        }        @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;            }        }    }

消息的类型进行了判断,如果这是一条MESSAGE_POST_RESULT消息,就会去执行finish()方法,如果这是一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。

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

InternalHandler的handleMessage()方法里,还有一种MESSAGE_POST_PROGRESS的消息类型,这种消息是用于当前进度的,调用的正是onProgressUpdate()方法,那么什么时候才会发出这样一条消息?

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

在doInBackground()方法中调用publishProgress()方法才可以从子线程切换到UI线程,从而完成对UI元素的更新操作。

说到底,AsyncTask也是使用的异步消息处理机制,只是做了非常好的封装而已。
读到这里,相信你对AsyncTask中的每个回调方法的作用、原理。

AsyncTask有什么缺陷?

工作线程是不能更新UI,同一个AsyncTask任务只能执行一次。最大支持128个线程的并发,10个任务的等待。

至此,AsyncTask源码分析完毕,希望您对AsyncTask有了更深的理解

源码下载地址,点击下载

3 0
原创粉丝点击