Android 源码解析AsyncTask的工作原理
1.1,AsyncTask的使用场景:开线程执行耗时的任务,并需要在主线程操作UI。值得一提的是,AsyncTask并不适用于做特别耗时的操作,特别耗时的操作可以用线程池+Handler来处理。
1.2,本篇文章着重讲述AsyncTask的原理,不再介绍具体如何使用AsyncTask。在使用AsyncTask时会介绍它的几个方法,这里先直接给出一些结论,后面会具体分析。方法如下:
voidonPreExecute():主线程中被调用,执行异步任务前被调用,用于做一些准备工作。
Result doInBackground(Params... params):子线程中被调用,用于做一些耗时的操作。参数params,调用execute方法时传入;返回值Result,作为参数传入onPostExecute方法。
void onPostExecute(Result result):主线程中被调用,用于操作UI。执行完异步任务后被调用,也就是doInBackground后被调用,参数result是方法doInBackground的返回值。
onProgressUpdate(Progress... values):主线程中被调用,用于操作UI,显示异步任务的执行进度。在异步任务执行过程中,也就是在doInBackground方法中调用publishProgress方法,该方法会被调用。
void onCancelled():异步任务被取消是调用,也就是cancel方法取消异步任务时被调用。
1.3,使用AsyncTask注意要点(后面具体分析):
1,AsyncTask类需要在主线程中完成加载,否则执行完异步任务后无法操作UI;
2,AsyncTask对象在主线程中创建,且execute方法也在主线程中执行。
3,上面提到那些方法均是被回调,不要在程序中直接调用。
4,Android1.6以前,AsyncTask是串行执行任务;在Android1.6~3.0版本里,AsyncTask采用了线程池并发执行任务;Android3.0开始,AsyncTask在原有线程池基础上,又添加了一个SerialExecutor线程池,用于串行执行任务,避免之前版本并发执行产生的同步问题。本篇文章是在Android3.0以后的版本进行分析,也就是AsyncTask串行执行任务。
5,AsyncTask对象只能调用一次execute方法,否则会抛出异常,也就是说一个AsyncTask对象只能执行一个任务。
二,AsyncTask源码分析在使用AsyncTask执行一个异步任务的时候,若是需要串行执行任务,可以调用AsyncTask的execute方法。在查看该方法前,先来看看AsyncTask构造方法做了一些什么。 查看源码如下:
public AsyncTask() { mWorker = new WorkerRunnable第2行,创建了一个WorkerRunnable的匿名子类对象mWorker。WorkerRunnable是AsyncTask的内部类,结构:private static abstract class WorkerRunnable() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked return postResult(doInBackground(mParams)); } }; mFuture = new FutureTask(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); } } }; } ,>
查看Callable源码:
public interface Callable { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }Callable接口只有一个call方法,由于不是本篇文章重点,这里就不深究Callable了,有兴趣的哥们可以在网上搜索Callable+Future的使用。
回到构造方法的第12行,创建FutureTask的匿名子类对象mFuture,且是有参的构造方法,参数mWorker就是WorkerRunnable的匿名子类对象。
查看其结构:public class FutureTask implements RunnableFuture ,public interface RunnableFuture extends Runnable, Future。也就是说,FutureTask实现了Runnable接口和Future接口,由于实现了Runnable接口,可以把FutureTask看做是一个任务。
接下来查看AsyncTask$execute方法源码:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; public final AsyncTask第6行,调用了executeOnExecutor方法,参数sDefaultExecutor是一个SerialExecutor对象,见第1,2行。值得一提是,SerialExecutor是AsyncTask的内部类。execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } //继续查看... public final AsyncTask ,>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; } ,>
第13行,在执行任务前,会对变量mStatus进行检查。mStatus有三种值:PENDING表示任务还没开始执行,RUNNING表示任务正在执行中,FINISHED表示任务已经执行完成。因此execute只能被调用一次,否则会抛出异常。
第25行,设置变量mStatus的值为RUNNING。
第27行,调用onPreExecute(),在任务执行前做一些准备工作,此时逻辑仍在主线程。
第30行,调用SerialExecutor$execute方法,且参数是FutureTask的匿名子类对象mFuture。
查看AsyncTask$SerialExecutor$execute方法源码:
private static class SerialExecutor implements Executor { final ArrayDeque mTasks = new ArrayDeque(); 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); } } }第2行,ArrayDeque是一个泛型的队列类,这里泛型声明的是Runnable,ArrayDeque可以对Runnable对象进行入队和出队的操作。例如,入队顺序是R1,R2,R3,那么出队顺序是R1,R2,R3。ArrayDeque并不是一个多么神秘的类,查看其结构的源码发现它本质是一个Collection集合,同样具有存储,删除,查询数据的操作。ArrayDeque提供了offer方法将数据对象放到队列的队尾,提供了poll方法将队首的数据对象移除出队列,并返回该数据对象。
第3行,定义一个Runnable对象,用于存放队列中取出的Runnable对象。
第6行,调用ArrayDeque$offer方法,将一个Runnable任务添加到队列里,这里称这个Runnable任务为M。需要注意的是,任务M并不是mFuture,它是只是一个普通的Runnable对象,里面封装了变量mFuture。看代码时,先不要管任务M里的这个run方法,只需要知道添加了一个任务M到队列里。
第15行,第一次调用AsyncTask$execute方法时,mActive为null,会调用scheduleNext()。
第21行,取出队列中处于队首的任务M,并将返回值也就是任务M,赋值给mActive。这样当在主线程中再次执行AsyncTask$execute方法时,新的任务M会加入队列,但由于mActive不为空,就不会调用scheduleNext()。那么新的任务M何时才能出队列呢,接着往下看。
第22行,使用THREAD_POOL_EXECUTOR线程池执行任务M,逻辑会执行到第7行的run方法。在该run方法里,执行了r.run(),也就是执行了mFuture的run方法。mFuture的run方法才是真正执行了异步任务(后面会具体分析),它是一个耗时操作,在执行完异步任务后才会执行finally里的代码,并不断重复该操作。
THREAD_POOL_EXECUTOR是一个什么样的线程池呢?查看其源码:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT + 1; private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE = 1; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); } }; private static final BlockingQueue sPoolWorkQueue = new LinkedBlockingQueue(128); /** * An {@link Executor} that can be used to execute tasks in parallel. */ public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);ThreadPoolExecutor是Java提供的API,它是接口Executor唯一的实现类,关于它的介绍见文章关于Android中常用的四种线程池的介绍,这里不再重复阐述。线程池THREAD_POOL_EXECUTOR有这样一些信息:核心线程数是cpu数加1,最大线程数是2倍的cpu数加1,非核心线程闲置超过1s会被系统回收,任务队列容量为128个。
值得一提的是,使用线程池执行一个新的任务时:
1,若线程池中仍有核心线程未使用,则启动核心线程来执行任务;
2,若核心线程均在执行任务,将任务放入工作队列中(工作队列未满);
3,若工作队列已满,启动一个非核心线程来执行任务(线程池中的线程未超过最大线程数);
4,若线程池中的线程超过最大线程数,任务被拒绝执行,默认是抛出异常给调用者。
小结:在Android3.0以后,调用AsyncTask$execute方法执行异步任务,是以串行的方式执行任务。
AsyncTask使用了SerialExecutor线程池,先将任务放在队列中,第一次执行任务会调用scheduleNext方法取出队列里任务,并采用THREAD_POOL_EXECUTOR线程池执行任务。在任务执行完毕后,才调用scheduleNext方法取出下一个任务并执行之,否则不会取出队列里的任务。
三,采用线程池THREAD_POOL_EXECUTOR,执行异步任务的过程前面讲到真正执行异步任务是由线程池THREAD_POOL_EXECUTOR完成,最终会调用变量mFuture的run方法。
mFuture是一个FutureTask类型的变量,查看FutureTask$run方法源码:
public FutureTask(Callable callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } public void run() { //... Callable c = callable; if (c != null && state == NEW) { //... result = c.call(); //... } //... }第13行,变量callable在构造函数中初始化,即前面提到的WorkerRunnable类型的对象mWorker。
第18行,调用Callable$call方法,即WorkerRunnable的call方法。
查看变量mWorker初始化的源码:
mWorker = new WorkerRunnable第7行,先执行doInBackground(mParams),并将其的返回值作为postResult方法的参数。需要注意的是,此时任务的执行仍在线程池THREAD_POOL_EXECUTOR中,因此doInBackground方法是在子线程中被调用,可以重写该方法并执行耗时操作。() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked return postResult(doInBackground(mParams)); } }; ,>
查看AsyncTask$postResult方法源码:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult(this, result)); message.sendToTarget(); return result; } //继续查看... private static class AsyncTaskResult { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; } }第3行,创建一个Message,消息的唯一标示what是MESSAGE_POST_RESULT,obj是AsyncTaskResult对象。AsyncTaskResult中封装了AsyncTask的引用,Result对象,至于为何要如此封装后面会给出解释。
第5行,发送消息,将任务的执行切换InternalHandler所在的线程。
查看InternalHandler源码:
private static final InternalHandler sHandler = new InternalHandler(); 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; } } }第1行,sHandler是一个静态类型的变量,执行完耗时操作后要操作UI,sHandler只能在主线程中创建,因此AsyncTask类在主线程中被加载。
第3行,InternalHandler是Handler的子类,它是AsyncTask中的静态内部类,在类加载的时候就完成了。
第7行,取出Message中的数据obj,是一个AsyncTaskResult类型的对象,AsyncTaskResult也是AsyncTask的一个静态内部类。
第11行,调用AsyncTaskResult的字段mTask获取AsyncTask的实例,并调用finish方法,此时在主线程中执行。这里必须要通过AsyncTaskResult这个静态内部类来获取,若直接使用this,由于类加载时this没有值,会编译出错。因此,需要采用AsyncTaskResult这个静态内部类,来封装AsyncTask的引用。
查看AsyncTask$finish方法源码:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }第3行,如果任务被取消,也就是调用cancel方法,则onCancelled被调用。我们可以重写onCancelled方法,把任务取消后想做操作放在里面。
第5行,调用onPostExecute方法,它是在doInBackground方法之后被执行。我们可以重写onPostExecute方法,将操作UI的代码放在该方法里。
第7行,将变量mStatus的值设置为FINISHED。
四,最后
关于调用publishProgress方法后,onProgressUpdate方法被调用的原因就不再分析了,比较容易分析,有兴趣哥们可以自行验证。
AsyncTask中封装了两个线程池和Handler,自定义的线程池SerialExecutor保证多个任务的串行执行,真正执行异步任务的是线程池THREAD_POOL_EXECUTOR。
Android3.0之后,若需要并行的方式执行任务,可以直接调用AsyncTask$executeOnExecutor(THREAD_POOL_EXECUTOR,params)。将任务的执行交给线程池THREAD_POOL_EXECUTOR,而不再需要线程池SerialExecutor。关于THREAD_POOL_EXECUTOR,前面已经做了详细分析了。
到这里,对AsyncTask工作原理的分析就结束了~ ^_^
推荐阅读
-
Android开发者必须要了解的View绘制过程(View的工作原理之Draw过程)
-
MapReduce原理(3): MapReduce的分片机制 getSplits()方法 源码解析
-
死磕Android_View工作原理你需要知道的一切
-
详解Android中用于线程处理的AsyncTask类的用法及源码
-
详解Android中用于线程处理的AsyncTask类的用法及源码
-
从源码解析Android中View的容器ViewGroup
-
Android AsyncTask完全解析 带你从源码的角度彻底理解
-
Android 中 SwipeLayout一个展示条目底层菜单的侧滑控件源码解析
-
从应用角度看Android源码 - 扒开AsyncTask的祖坟
-
从源码解析Android中View的容器ViewGroup