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有了更深的理解
源码下载地址,点击下载
- AsyncTask使用与源码解析(二)
- AsyncTask使用与源码解析
- AsyncTask源码解析(二)
- AsyncTask使用及源码解析
- AsyncTask源码解析,简单使用
- AsyncTask使用和源码解析
- AsyncTask源码解析二-----工作线程
- Android 源码解析AsyncTask(二)
- Android AsyncTask使用步骤与源码分析
- AsyncTask源码解析
- AsyncTask源码解析
- Android AsyncTask源码解析
- AsyncTask源码解析
- AsyncTask源码解析
- Android AsyncTask 源码解析
- Android AsyncTask 源码解析
- Android AsyncTask 源码解析
- Android AsyncTask 源码解析
- 二维码生成技术
- Butterknife 8.5.1使用-全面,一定要看
- 三种java实现多线程的方式
- TCP/IP包头详解
- 学习笔记C语言 open fgets strstr strchr strncpy snprintf函数
- AsyncTask使用与源码解析(二)
- Scrapy 报错 “No module named win32api”的解决方法
- 开启我的博客之旅
- TCP包头详解
- 千位数换算的封装
- DBus如何传送和接收数据,包括结构体
- 380. Insert Delete GetRandom O(1)
- 免费馅饼(动态规划)
- Android翻书翻页(支持硬翻软翻)