AsyncTask使用与源码解析
Posted 单灿灿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AsyncTask使用与源码解析相关的知识,希望对你有一定的参考价值。
本文承接于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使用与源码解析的主要内容,如果未能解决你的问题,请参考以下文章