[转]【安卓笔记】AsyncTask源码剖析

http://blog.csdn.net/chdjj/article/details/39122547

前言:

初学AsyncTask时,就想研究下它的实现源码,怎奈源码看了好几遍都没看懂,于是搁置了。最近心血来潮,又看了一些源码,如 HandlerThread,IntentService,AsyncQueryHandler等,收获颇深,于是乎想回头再研究下AsyncTask, 没想到这次居然很容易看懂了。。。
正文:
注:1.读者阅读本文前,必须对android的Handler机制以及j.u.c中的线程池有所了解;2.AsyncTask使用方式不再赘述。3.不同版本AsyncTask内容有些不同.
首先明确AsyncTask是个抽象类,接受三个泛型参数,分表代表任务所需参数类型,任务进度类型,结果类型。

  1. public abstract class AsyncTask<Params, Progress, Result>
开发者继承AsyncTask后必须重写doInbackground方法,其他方法如onPostExecute等按需重写。
其内部有个静态全局的线程池变量THREAD_POOL_EXECUTOR,AsyncTask的doInbackground中的任务就是由此线程池执行。
  1. public static final Executor THREAD_POOL_EXECUTOR
  2. = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
  3. TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

ThreadPoolExecutor
各参数含义如下,CORE_POOL_SIZE为核心线程数量,MAXIMUM_POOL_SIZE为线程池最大线程数量,KEEP_ALIVE参数表明
当线程池的线程数量大于核心线程数量,那么空闲时间超过KEEP_ALIVE时,超出部分的线程将被回收,sPoolWorkQueue是任务队列,存储
的是一个个Runnable,sThreadFactory是线程工厂,用于创建线程池中的线程。所有这些参数在AsyncTask内部都已经定义好:

  1. private static final int CORE_POOL_SIZE = 5;
  2. private static final int MAXIMUM_POOL_SIZE = 128;
  3. private static final int KEEP_ALIVE = 1;
  4. private static final ThreadFactory sThreadFactory = new ThreadFactory() {
  5. private final AtomicInteger mCount = new AtomicInteger(1);
  6. public Thread newThread(Runnable r) {
  7. return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
  8. }
  9. };
  10. private static final BlockingQueue<Runnable> sPoolWorkQueue =
  11. new LinkedBlockingQueue<Runnable>(10);

AsyncTask并不是直接使用上述线程池,而是进行了一层“包装”,这个类就是SerialExecutor,这是个串行的线程池。

  1. /**
  2. * An {@link Executor} that executes tasks one at a time in serial
  3. * order.  This serialization is global to a particular process.
  4. */
  5. public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

看其具体实现:

  1. private static class SerialExecutor implements Executor {
  2. final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
  3. Runnable mActive;
  4. public synchronized void execute(final Runnable r) {
  5. mTasks.offer(new Runnable() {
  6. public void run() {
  7. try {
  8. r.run();
  9. } finally {
  10. scheduleNext();
  11. }
  12. }
  13. });
  14. if (mActive == null) {
  15. scheduleNext();
  16. }
  17. }
  18. protected synchronized void scheduleNext() {
  19. if ((mActive = mTasks.poll()) != null) {
  20. THREAD_POOL_EXECUTOR.execute(mActive);
  21. }
  22. }
  23. }
调用execute方法时,先将Runnable包装一下(即加上try-finally块)然后加入队列,接着判断当前mActive是否为
空,第一次调用,此值为空,所以会调用scheduleNext方法,从队列中取出头部的任务,交给线程池THREAD_POOL_EXECUTOR处
理,而处理过程是这样的,先执行原先的Runnable的run方法,接着再执行scheduleNext从队列中取出Runnable,如此循环,直到
队列为空,mActive重新为空为止。我们发现,经过这样的处理,所有任务将串行执行
所以我们需要注意,如果两个AsyncTask都调用execute时,如果其中一个AsyncTask任务执行时间非常长,这将导致另一个AsyncTask的任务排队等候,无法执行,因为它们共享同一个线程池且池是串行执行任务的。
接着看其他成员:
  1. private static final int MESSAGE_POST_RESULT = 0x1;//当前消息类型--->任务完成消息
  2. private static final int MESSAGE_POST_PROGRESS = 0x2;//当前消息类型-->进度消息
  3. private static final InternalHandler sHandler = new InternalHandler();
  4. private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;//默认线程池
  5. private final WorkerRunnable<Params, Result> mWorker;
  6. private final FutureTask<Result> mFuture;
  7. private volatile Status mStatus = Status.PENDING;//当前状态
  8. private final AtomicBoolean mCancelled = new AtomicBoolean();
  9. private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

sDefaultExecutor指明默认的线程池是串行池,mStatus指明当前任务状态,Status是个枚举类型:

  1. public enum Status {
  2. /**
  3. * Indicates that the task has not been executed yet.
  4. */
  5. PENDING,//等待执行
  6. /**
  7. * Indicates that the task is running.
  8. */
  9. RUNNING,//执行
  10. /**
  11. * Indicates that {@link AsyncTask#onPostExecute} has finished.
  12. */
  13. FINISHED,//执行结束
  14. }

重点看InternalHandler实现:

  1. private static class InternalHandler extends Handler {
  2. @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
  3. @Override
  4. public void handleMessage(Message msg) {
  5. AsyncTaskResult result = (AsyncTaskResult) msg.obj;
  6. switch (msg.what) {
  7. case MESSAGE_POST_RESULT:
  8. // There is only one result
  9. result.mTask.finish(result.mData[0]);
  10. break;
  11. case MESSAGE_POST_PROGRESS:
  12. result.mTask.onProgressUpdate(result.mData);
  13. break;
  14. }
  15. }
  16. }

InternalHandler继承自Handler,并复写了handleMessage,该方法根据消息类型做不同处理,如
果任务完成,则调用finish方法,而finish方法根据任务状态(取消or完成)调用onCancelled或者onPostExecute,这两
个是回调方法,通常我们会在onPostExecute中根据任务执行结果更新UI,这也是为什么文档中要求AsyncTask必须在UI线程中创建的原
因,我们的Handler须与UI线程的Looper绑定才能更新UI,并且子线程默认是没有Looper的。

  1. private void finish(Result result) {
  2. if (isCancelled()) {
  3. onCancelled(result);
  4. } else {
  5. onPostExecute(result);
  6. }
  7. mStatus = Status.FINISHED;
  8. }
如果是更新进度的消息(MESSAGE_POST_PROGRESS),那么调用onProgressUpdate方法。
接下来我们关注的问题是doInbackground方法在何处被调用?联想AsyncTask的使用,当我们创建好一个AsyncTask实例后,我们将调用其execute方法,所以,doInbackground方法应该在execute方法中执行,找到其实现:
  1. public final AsyncTask<Params, Progress, Result> execute(Params... params) {
  2. return executeOnExecutor(sDefaultExecutor, params);
  3. }

并没有直接调用doInbackground,而是调用了executeOnExecutor方法:

  1. public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
  2. Params... params) {
  3. if (mStatus != Status.PENDING) {
  4. switch (mStatus) {
  5. case RUNNING:
  6. throw new IllegalStateException("Cannot execute task:"
  7. + " the task is already running.");
  8. case FINISHED:
  9. throw new IllegalStateException("Cannot execute task:"
  10. + " the task has already been executed "
  11. + "(a task can be executed only once)");
  12. }
  13. }
  14. mStatus = Status.RUNNING;
  15. onPreExecute();
  16. mWorker.mParams = params;
  17. exec.execute(mFuture);
  18. return this;
  19. }
executeOnExecutor方法的作用是使用指定的线程池执行任务,这里当然使用的是sDefaultExecutor,也就是SerialExecutor,但是我们注意到这个方法是公共的,也就是说我们可以手动配置线程池,让AsyncTask并行执行起来,最简单的方法就是使用内部定义好的THREAD_POOL_EXECUTOR。
executeOnExecutor方法首先检查当前状态,如果不是Pending状态则抛出异常,紧接着修改状态为Running态,接着调用onPreExecute方法(预处理,相信大家不陌生)。
关键代码是exec.execute(mFuture)这一句,这行代码的作用是执行mFuture中定义好的任务,mFuture为
FutureTask类型,FutureTask是Runnable的实现类(j.u.c中的),所以可以作为线程池execute方法的参数,我们找到
其定义:
  1. mFuture = new FutureTask<Result>(mWorker) {
  2. @Override
  3. protected void done() {
  4. try {
  5. postResultIfNotInvoked(get());
  6. } catch (InterruptedException e) {
  7. android.util.Log.w(LOG_TAG, e);
  8. } catch (ExecutionException e) {
  9. throw new RuntimeException("An error occured while executing doInBackground()",
  10. e.getCause());
  11. } catch (CancellationException e) {
  12. postResultIfNotInvoked(null);
  13. }
  14. }
  15. };

我们都知道,FutureTask构造时必须传入一个Callable的实现类,线程最终执行的是Callable的call方法(不明白的请看java线程并发库),所以mWorker必然是Callable的实现类:

  1. private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
  2. Params[] mParams;
  3. }
  4. Worker = new WorkerRunnable<Params, Result>() {
  5. public Result call() throws Exception {
  6. mTaskInvoked.set(true);
  7. Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  8. //noinspection unchecked
  9. return postResult(doInBackground(mParams));
  10. }
  11. };
最终,我们在WorkRunnable方法中找到了doInBackground方法,历经艰辛啊!!
剩下最后一个问题:消息是如何发送给Handler的?
1.任务执行完毕的消息是通过postResult方法发送的:
  1. private Result postResult(Result result) {
  2. @SuppressWarnings("unchecked")
  3. Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
  4. new AsyncTaskResult<Result>(this, result));
  5. message.sendToTarget();
  6. return result;
  7. }

这个AsyncTaskResult封装了数据域和对象本身:

  1. private static class AsyncTaskResult<Data> {
  2. final AsyncTask mTask;
  3. final Data[] mData;
  4. AsyncTaskResult(AsyncTask task, Data... data) {
  5. mTask = task;
  6. mData = data;
  7. }
  8. }

2.任务进度更新消息是通过publishProgress方法发送的:

  1. protected final void publishProgress(Progress... values) {
  2. if (!isCancelled()) {
  3. sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
  4. new AsyncTaskResult<Progress>(this, values)).sendToTarget();
  5. }
  6. }
至此,AsyncTask源码分析完毕!
稍微做个总结
1.AsyncTask是对线程池和Handler的封装,但是默认情况下任务是串行执行的,需注意,多个AsyncTask实例共享同一线程池(线程池是static的);
2.高并发任务并不推荐使用AsyncTask,而应该改用线程池;
3.务必在主线程中创建AsyncTask,因为AsyncTask内部的Handler在创建时必须要绑定主线程的Looper;
4.只能在doInBackground方法中执行耗时任务,其他方法如onPreExecute、onPostExecute等运行于主线程上.

[转]【安卓笔记】AsyncTask源码剖析的更多相关文章

  1. [笔记]LibSVM源码剖析(java版)

    之前学习了SVM的原理(见http://www.cnblogs.com/bentuwuying/p/6444249.html),以及SMO算法的理论基础(见http://www.cnblogs.com ...

  2. STL源码剖析读书笔记之vector

    STL源码剖析读书笔记之vector 1.vector概述 vector是一种序列式容器,我的理解是vector就像数组.但是数组有一个很大的问题就是当我们分配 一个一定大小的数组的时候,起初也许我们 ...

  3. AsyncTask源码笔记

    AsyncTask源码笔记 AsyncTask在注释中建议只用来做短时间的异步操作,也就是只有几秒的操作:如果是长时间的操作,建议还是使用java.util.concurrent包中的工具类,例如Ex ...

  4. 【安卓网络请求开源框架Volley源码解析系列】定制自己的Request请求及Volley框架源码剖析

    通过前面的学习我们已经掌握了Volley的基本用法,没看过的建议大家先去阅读我的博文[安卓网络请求开源框架Volley源码解析系列]初识Volley及其基本用法.如StringRequest用来请求一 ...

  5. c++ stl源码剖析学习笔记(一)uninitialized_copy()函数

    template <class InputIterator, class ForwardIterator>inline ForwardIterator uninitialized_copy ...

  6. Hadoop源码学习笔记之NameNode启动场景流程二:http server启动源码剖析

    NameNodeHttpServer启动源码剖析,这一部分主要按以下步骤进行: 一.源码调用分析 二.伪代码调用流程梳理 三.http server服务流程图解 第一步,源码调用分析 前一篇文章已经锁 ...

  7. 《STL源码剖析》读书笔记

    转载:https://www.cnblogs.com/xiaoyi115/p/3721922.html 直接逼入正题. Standard Template Library简称STL.STL可分为容器( ...

  8. 通读《STL源码剖析》之后的一点读书笔记

    直接逼入正题. Standard Template Library简称STL.STL可分为容器(containers).迭代器(iterators).空间配置器(allocator).配接器(adap ...

  9. ffmpeg/ffplay源码剖析笔记<转>

    转载:http://www.cnblogs.com/azraelly/ http://www.cnblogs.com/azraelly/archive/2013/01/18/2865858.html ...

随机推荐

  1. lamp环境部署脚本

    关于lamp环境的安装脚本,直接复制即可使用 注:apache2.2.X 版本和apache2.4.X版本 本人推荐兼容性版本安装 apache2.4.25 + apr1.5.2 + apr-util ...

  2. bzoj 4596 [Shoi2016]黑暗前的幻想乡 矩阵树定理+容斥

    4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 559  Solved: 325[Submit][Sta ...

  3. Jmeter简单介绍与搭配Jenkins实现自动化

    Jmeter简介 Apache JMeter 是 Apache 组织开发的基于 Java 的压力测试工具.用于对软件做压力测试,它最初被设计用于 Web应用测试,但后来扩展到其他测试领域. 它可以用于 ...

  4. Qt Creator 整合 python 解释器教程

    目录 1. 前言 2.前提条件 3.步骤 3.1 新建 python文件 3.2 编写 python 代码 3.3 配置 python 解释器 3.4 执行 python file 1. 前言 Pyt ...

  5. 面向对象的线程池Threadpool的封装

    线程池是一种多线程处理形式,预先创建好一定数量的线程,将其保存于一个容器中(如vector), 处理过程中将任务添加到队列,然后从容器中取出线程后自动启动这些任务,具体实现如下. 以下是UML图,展示 ...

  6. 一个可以自由定制外观、支持拖拽消除的MaterialDesign风格Android BadgeView

    为了尊重作者,先放上链接:https://github.com/qstumn/BadgeView BadgeView 一个可以自由定制外观.支持拖拽消除的MaterialDesign风格Android ...

  7. FFmpeg-音频和视频应用程序的瑞士军刀

    FFmpeg是一个开源免费跨平台的视频和音频流方案,属于自由软件,采用LGPL或GPL许可证(依据你选择的组件).它提供了录制.转换以及流化音视频的完整解决方案.它包含了非常先进的音频/视频编解码库l ...

  8. Sparklyr与Docker的推荐系统实战

    作者:Harry Zhu 链接:https://zhuanlan.zhihu.com/p/21574497 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 相关内容: ...

  9. 基于Jenkins+Git+Gradle的Android持续集成

    本文参考了: http://my.oschina.net/uboluo/blog/157483 http://java.dzone.com/articles/automating-continuous ...

  10. NLP︱LDA主题模型的应用难题、使用心得及从多元统计角度剖析

    将LDA跟多元统计分析结合起来看,那么LDA中的主题就像词主成分,其把主成分-样本之间的关系说清楚了.多元学的时候聚类分为Q型聚类.R型聚类以及主成分分析.R型聚类.主成分分析针对变量,Q型聚类针对样 ...