在Android中有多种实现多线程的方式,比如AsyncTask、HandlerThread、IntentService等,其实从本质上讲,它们都是对传统线程Thread的封装。AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和结果传递给主线程并在主线程中更新UI。从实现上讲,AsyncTask封装了Thread和Handler,通过AsyncTask可以更加方便地执行后台任务以及在主线程中访问UI,但是AsyncTask并不适合进行特别耗时的后台任务,对特别耗时的任务来说,建议使用其他形式的线程池。

AsyncTask是一个抽象的泛型类,它提供了Params、Progress、Result这三个泛型参数,其中Params表示参数的类型,Progress表示进度的类型,Result表示返回结果的类型,如果不需要传递具体的参数,那么这三个泛型参数可以用Void来代替。AsyncTask声明如下:

public abstract class AsyncTask<Params, Progress, Result>

AsyncTask提供了4个核心方法,分别如下:

  • onPreExecute(),在主线程中执行,在异步任务执行之前,此方法会被调用,一般可用于做一些准备工作。
  • doInBackground(Params... params),在线程池中执行,此方法用于执行异步任务,Params参数表示异步任务的输入参数。在此方法中可以通过publishProgress方法来更新任务进度。publishProgress方法会调用onProgressUpdate方法。另外此方法需要返回结果给onPostExecute(Result  result)方法。
  • onProgressUpdate(Progress progress),在主线程中执行,当后台任务的执行进度发生改变时,此方法会被调用。
  • onPostExecute(Result result),在主线程中执行,在异步任务执行之后,此方法会被调用,其中result参数是后台任务的返回值,即doInbackground方法的返回值。

上面四个方法的执行顺序是 onPreExecute-> doInBackground-> onProgressUpdate-> onPostExecute。

除了上述四个方法外,还提供了onCanceled方法,同样是在主线程中执行,当异步任务被取消时,onCanceled方法会被调用,这个时候onPostExecute则不会被调用。下面是示例代码:

public class DownloadFileTask extends AsyncTask<URL, Integer, Long> {@Overrideprotected void onPreExecute() {super.onPreExecute();}@Overrideprotected Long doInBackground(URL... urls) {int count = urls.length;long totalSize = 0;for (int i = 0; i < count; i++) {totalSize += Downloader.downloadFile(urls[0]);publishProgress((int) ((i / (float) count) * 100));if(isCancelled()) {break;}}return totalSize;}@Overrideprotected void onProgressUpdate(Integer... values) {setProgressPercent(values[0]);}@Overrideprotected void onPostExecute(Long aLong) {showDialog("Downloaded" + result + "bytes");}
}

在上面的代码块中,实现了一个具体的AsyncTask类,。这个类主要用于模拟文件的下载过程,它的输入类型为URL,后台任务的进度参数类型是Interger,返回结果的类型是Long。doInBackground用来执行具体的下载任务并通过publish方法来更新下载的进度,同事还要判断任务是否被取消。当下载任务完成后,doInBackground返回结果,它是在线程池中执行的。onProgressUpdate用于更新下载进度显示,运行在主线程中,当publishProgess方法被执行时这个方法就会被调用。当下载任务完成后,onPostExecute方法就会被调用,也是运行在主线程中,在这里可以做下载完成的提示。

AsyncTask在具体的使用过程中也有一些条件显示,主要又如下几点:

  • AsyncTask的类必须在主线程中加载,这就意味着第一次访问AsyncTask必须发生在主线程
  • AsyncTask对象必须在主线程中创建
  • execute方法必须在UI线程中调用
  • 不要再程序中直接调用onPreExecute、doInBackground、onProgressUpdate、onPostExecute方法
  • 一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则抛出异常
  • 为了避免并发错误,AsyncTask默认采用一个线程串行执行任务,但仍然可以通过executeOnExecutor方法来并行执行任务。

其实,从Android5.1后,已经AsyncTask的初始化可以不用再主线程中执行,这是因为Android5.1后InternalHandler的实现已经发生改变,之前是这么定义的sHandler和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;
            }
        }
    }

由于sHandler的作用是将执行环境切换到主线程,所以sHandler构建的时候需要MainLooper,而静态成员会在类加载的时候进行初始化,因此变相要求AsyncTask的类必须在主线程中加载,否则同一进程的AsyncTask都无法正常工作,所以在ActivityThread的main()方法中有调用AsyncTask.init()方法,来强制sHandler被创建。在5.1版本后,getHandler方法是一个静态方法,其中保证了sHandler是单例的,在sHandler初始化是传入了Looper.getMainLooper(),保证了sHandler中looper就是主线程的looper,从而保证sHandler能够将执行环境切换到主线程中:

private static Handler getMainHandler() {synchronized (AsyncTask.class) {if (sHandler == null) {sHandler = new InternalHandler(Looper.getMainLooper());}return sHandler;}
}
private static class InternalHandler extends Handler {public InternalHandler(Looper looper) {super(looper);}@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic void handleMessage(Message msg) {AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;switch (msg.what) {case MESSAGE_POST_RESULT:// There is only one resultresult.mTask.finish(result.mData[0]);break;case MESSAGE_POST_PROGRESS:result.mTask.onProgressUpdate(result.mData);break;}}
}

为了分析AsyncTask的工作原理,需要从execute方法开始分析,execute方法又会调用executeOnExecutor方法,如下:

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> 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;
}

在上面的代码中,sDefaultExector实际上是一个串行的线程池,一个进程中所有的AsyncTask全部在这个串行的线程池中排队执行,这个排队执行的过程后面会再进行分析。在executeOnExector方法中,AsyncTask的onPreExecutor方法最先执行,然后线程池开始执行。下面是线程池的执行过程:

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();}}protected synchronized void scheduleNext() {if ((mActive = mTasks.poll()) != null) {THREAD_POOL_EXECUTOR.execute(mActive);}}
}

从SerialExecutor的实现可以分析AsyncTask的排队执行的过程。首先系统会把AsyncTask的Params参数封装到FutureTask对象,FutureTask是一个并发类,在这里它充当了Runnable角色。接着这个FutureTask会交割SerialExector的execute方法去处理,SerialExecutor的execute方法首先会把FutureTask对象插入到任务队列mTasks中,如果这个时候没有正在活动的AsyncTask任务,那么就会调用SerialExecutor的scheduleNext方法来执行下一个任务,同时当一个AsyncTask任务执行完后,AsyncTask会继续执行其他任务直到所有的任务都被执行为止,在默认情况下AsyncTask是串行执行的。

从上面代码可以看出,SerialExecutor这个线程池使用存储任务队列的,真正执行任务的是THREAD_POOL_EXECUTOR,AsyncTask的内部类InnerHandler用于切换到UI线程。

mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {mTaskInvoked.set(true);Result result = null;try {Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//noinspection uncheckedresult = doInBackground(mParams);Binder.flushPendingCommands();} catch (Throwable tr) {mCancelled.set(true);throw tr;} finally {postResult(result);}return result;}
};

从上面代码可以看到,doInBackground方法最终是在WorkerRunnable方法的run()方法中调用的,而WorkerRunnable的call方法又在上面说到的FutureTask的run方法中被调用。在mWorker的call方法中,首先将mTaskInvoked设置为true,表示当前任务已经被调用过了,然后执行doInBackground方法,接着将其结果传递给postResult(result),可以看到,postResult其实是发送了个消息到InnerHandler中:

private Result postResult(Result result) {@SuppressWarnings("unchecked")Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));message.sendToTarget();return result;
}

看下Handler中是怎么处理的:

private static class InternalHandler extends Handler {public InternalHandler(Looper looper) {super(looper);}@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic void handleMessage(Message msg) {AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;switch (msg.what) {case MESSAGE_POST_RESULT:// There is only one resultresult.mTask.finish(result.mData[0]);break;case MESSAGE_POST_PROGRESS:result.mTask.onProgressUpdate(result.mData);break;}}
}

收到消息后,会执行finish方法,代码如下,最终onPostExecute被调用,如果任务被取消的话则会调用onCancelled方法

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

publishProgress过程类似,也是通过sHandler切换到主线程中的。

AsyncTask详解相关推荐

  1. Android 多线程-----AsyncTask详解

    您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...

  2. [Android] AsyncTask详解

    2019独角兽企业重金招聘Python工程师标准>>> 本篇随笔将讲解一下Android的多线程的知识,以及如何通过AsyncTask机制来实现线程之间的通信. 一.Android当 ...

  3. Android AsyncTask 详解及注意事项

    AsyncTask是Android提供的轻量级的异步类,它使创建异步任务变得更加简单,不再需要编写任务线程和Handler实例即可完成相同的任务. AsyncTask定义了三种泛型类型 Params, ...

  4. Android AsyncTask详解

    前言 一提到多线程,我们不得不提到AsyncTask,很多Android开发人员在网络请求这块,一般会使用开源框架,比如Volley,Okhttp,retrofit等.但是有一部分人,比较忠于封装As ...

  5. android asynctask 参数,Android中AsyncTask详解

    定义 AsyncTask是一个抽象类,在使用时需要继承该类,实现其抽象方法protected abstract Result doInBackground(Params... params).其主要作 ...

  6. AsyncTask 详解

    AsyncTask 简介 AsyncTask,即异步任务,是 Android 给我们提供的一个处理异步任务的类.通过 此类,可以实现主线程和后台线程的通讯.后台线程执行异步任务,并把结果返回 给主线程 ...

  7. Android 多线程-----AsyncTask详解(康小岱已读)

    本篇随笔将讲解一下Android的多线程的知识,以及如何通过AsyncTask机制来实现线程之间的通信. 一.Android当中的多线程 在Android当中,当一个应用程序的组件启动的时候,并且没有 ...

  8. Android 多线程AsyncTask详解

    原文地址:http://www.cnblogs.com/xiaoluo501395377/p/3430542.html 本篇随笔将讲解一下Android的多线程的知识,以及如何通过AsyncTask机 ...

  9. Android之AsyncTask异步任务详解总结

    Android 多线程----AsyncTask异步任务详解 [正文] 本文将讲解一下Android的多线程的知识,以及如何通过AsyncTask机制来实现线程之间的通信. 一.Android当中的多 ...

最新文章

  1. win32ctypes.pywin32.pywintypes.error: (2, ‘LoadLibraryEx‘, ‘系统找不到指定的文件。‘)
  2. 局域网指定 IP 地址后无法上网的问题
  3. 【软件工程】CMMI 能力成熟度模型集成 ( CMMI 过程管理过程域 | CMMI 项目管理过程域 ) ★
  4. Sed教程(四):基本命令、特殊字符、字符串
  5. hutool的定时任务不支持依赖注入怎么办_「架构」 - 定时任务 amp; Elastic-Job基本使用...
  6. 正则表达式的深入理解
  7. SpringCloud Gateway的组成结构
  8. 3.Contructor(构造器)模式—精读《JavaScript 设计模式》Addy Osmani著
  9. Server 2008 R2 AD RMS完整部署:AD部署篇
  10. 如何在python中获取浮点数的十六进制值?
  11. 重磅!ICCV 2019 COCO + Mapillary 联合识别挑战赛开启!
  12. 选择排序--Selection sort
  13. Delphi 集合和字符串互转
  14. 自动化之RPA工具之UiPath
  15. 维基百科语料库训练词向量
  16. sqrt mysql_详解MySQL中的SQRT函数的使用方法_MySQL
  17. centos 6 升级gcc
  18. 全球与中国生物质颗粒市场深度研究分析报告
  19. [云上贵州2017]智慧交通预测挑战赛
  20. jQuery EasyUI详解-EasyUI环境配置

热门文章

  1. HTTP 状态码详解大全 | HTTP Status Codes
  2. 用python输入三个整数判断能否构成三角形_用户输入三个数字,判断可以构成什么三角形?...
  3. 渗透测试各阶段工具速查(持续更新)
  4. 计算机应用网上购物摘要,计算机应用软件毕业设计 --网上购物商城.doc
  5. ROS系列——image-transport功能包没有发布compressed图像Topic的原因
  6. PhotoShop---墨迹字体+烟雾效果
  7. 一个很重要但很多人不理解的技术,P2P丨网络穿透与NAT原理分析
  8. 《android进阶之光》——事件总线(下)
  9. cocos2dx-4.文本
  10. java pattern 手机号_业余草 Java正则表达式,验证手机号和电话号码