前言

AsyncTask是Android提供的一个助手类,它对Thread和Handler进行了封装,方便我们使用;
Android之所以提供AsyncTask这个类,就是为了方便我们在后台线程中执行操作,然后将结果发送给主线程,从而在主线程中进行UI更新等操作。在使用AsyncTask时,我们无需关注Thread和Handler,AsyncTask内部会对其进行管理,这样我们就只需要关注于我们的业务逻辑即可;

一、AsyncTask介绍和使用

1、AsyncTask介绍

AsyncTask有四个重要的回调方法,分别是:onPreExecute、doInBackground, onProgressUpdate 和 onPostExecute。这四个方法会在AsyncTask的不同时期进行自动调用,我们只需要实现这几个方法的内部逻辑即可;

这四个方法的一些参数和返回值都是基于泛型的,而且泛型的类型还不一样,所以在AsyncTask的使用中会遇到三种泛型参数:Params, Progress 和 Result;

public abstract class AsyncTask<Params, Progress, Result>

AsyncTask是一个抽象泛型类。

其中,三个泛型类型参数的含义如下:

  • Params:开始异步任务执行时传入的参数类型;

  • Progress:异步任务执行过程中,返回下载进度值的类型;

  • Result:异步任务执行完成后,返回的结果类型

2、AsyncTask的简单使用

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {  @Override  protected void onPreExecute() {  progressDialog.show();  }  @Override  protected Boolean doInBackground(Void... params) {  try {  while (true) {  int downloadPercent = doDownload();  publishProgress(downloadPercent);  if (downloadPercent >= 100) {  break;  }  }  } catch (Exception e) {  return false;  }  return true;  }  @Override  protected void onProgressUpdate(Integer... values) {  progressDialog.setMessage("当前下载进度:" + values[0] + "%");  }  @Override  protected void onPostExecute(Boolean result) {  progressDialog.dismiss();  if (result) {  Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();  } else {  Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();  }  }  }

模拟了一个下载任务,在doInBackground()方法中去执行具体的下载逻辑,在onProgressUpdate()方法中显示当前的下载进度,在onPostExecute()方法中来提示任务的执行结果。如果想要启动这个任务,只需要简单地调用以下代码即可:

new DownloadTask().execute();

3、使用AsyncTask的注意事项

①异步任务的实例必须在UI线程中创建,即AsyncTask对象必须在UI线程中创建;

②execute(Params… params)方法必须在UI线程中调用;

③不要手动调用onPreExecute(),doInBackground(Params… params),onProgressUpdate(Progress… values),onPostExecute(Result result)这几个方法;

④不能在doInBackground(Params… params)中更改UI组件的信息;

⑤一个任务实例只能执行一次,如果执行第二次将会抛出异常;

二、AsyncTask的源码分析

1、AsyncTask的构造函数

/*** 具体使用*/MyTask mTask = new MyTask();/*** 源码分析:AsyncTask的构造函数*/public AsyncTask() {// 初始化WorkerRunnable变量 = 一个可存储参数的Callable对象 mWorker = new WorkerRunnable<Params, Result>() {// 在任务执行线程池中回调:THREAD_POOL_EXECUTOR.execute()// 下面会详细讲解public Result call() throws Exception {// 添加线程的调用标识mTaskInvoked.set(true); Result result = null;try {// 设置线程的优先级Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);// 执行异步操作 = 耗时操作// 即 我们使用过程中复写的耗时任务result = doInBackground(mParams);Binder.flushPendingCommands();} catch (Throwable tr) {mCancelled.set(true);// 若运行异常,设置取消的标志throw tr;} finally {// 把异步操作执行的结果发送到主线程// 从而更新UI,下面会详细讲解postResult(result); }return result;}};//  初始化FutureTask变量 = 1个FutureTask mFuture = new FutureTask<Result>(mWorker) {// done()简介:FutureTask内的Callable执行完后的调用方法// 作用:复查任务的调用、将未被调用的任务的结果通过InternalHandler传递到UI线程@Overrideprotected void done() {try {// 在执行完任务后检查,将没被调用的Result也一并发出 postResultIfNotInvoked(get());} catch (InterruptedException e) {android.util.Log.w(LOG_TAG, e);} catch (ExecutionException e) {throw new RuntimeException("An error occurred while executing doInBackground()",e.getCause());} catch (CancellationException e) {//若 发生异常,则将发出nullpostResultIfNotInvoked(null);}}};}/**WorkerRunnable类的构造函数*/private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {// 此处的Callable也是任务;// 与Runnable的区别:Callable<T>存在返回值 = 其泛型Params[] mParams;}/*** 分析:FutureTask类的构造函数* 定义:1个包装任务的包装类* 注:内部包含Callable<T> 、增加了一些状态标识 & 操作Callable<T>的接口*/public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW;      }// 回到调用原处/*** postResultIfNotInvoked()*/private void postResultIfNotInvoked()(Result result) {// 取得任务标记final boolean wasTaskInvoked = mTaskInvoked.get();// 若任务无被执行,将未被调用的任务的结果通过InternalHandler传递到UI线程if (!wasTaskInvoked) {postResult(result);}}
  • 创建了1个WorkerRunnable类 的实例对象 & 复写了call()方法;

  • 创建了1个FutureTask类 的实例对象 & 复写了 done();

2、execute(Params… params)

/*** 具体使用*/mTask.execute();/*** 源码分析:AsyncTask的execute()*/public final AsyncTask<Params, Progress, Result> execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);// ->>分析1}/*** executeOnExecutor(sDefaultExecutor, params)* 参数说明:sDefaultExecutor = 任务队列 线程池类(SerialExecutor)的对象*/public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {//  判断 AsyncTask 当前的执行状态// PENDING = 初始化状态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)");}}//  将AsyncTask状态设置为RUNNING状态mStatus = Status.RUNNING;//  主线程初始化工作onPreExecute();//  添加参数到任务中mWorker.mParams = params;//  执行任务// 此处的exec = sDefaultExecutor = 任务队列 线程池类(SerialExecutor)的对象exec.execute(mFuture);return this;}/*** exec.execute(mFuture)* 说明:属于任务队列 线程池类(SerialExecutor)的方法*/private static class SerialExecutor implements Executor {// SerialExecutor = 静态内部类// 即 是所有实例化的AsyncTask对象公有的// SerialExecutor 内部维持了1个双向队列;// 容量根据元素数量调节final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();Runnable mActive;// execute()被同步锁synchronized修饰// 即说明:通过锁使得该队列保证AsyncTask中的任务是串行执行的// 即 多个任务需1个个加到该队列中;然后 执行完队列头部的再执行下一个,以此类推public synchronized void execute(final Runnable r) {// 将实例化后的FutureTask类 的实例对象传入// 即相当于:向队列中加入一个新的任务mTasks.offer(new Runnable() {public void run() {try {r.run();} finally {scheduleNext();}}});// 若当前无任务执行,则去队列中取出1个执行if (mActive == null) {scheduleNext();}}// protected synchronized void scheduleNext() {// 取出队列头部任务if ((mActive = mTasks.poll()) != null) {//  执行取出的队列头部任务// 即调用执行任务线程池类(THREAD_POOL_EXECUTOR)THREAD_POOL_EXECUTOR.execute(mActive);}}}
  • 执行任务前,通过 任务队列 线程池类(SerialExecutor)将任务按顺序放入到队列中;

  • 通过同步锁修饰execute()从而保证AsyncTask中的任务是串行执行的;

  • 之后的线程任务执行是 通过任务线程池类(THREAD_POOL_EXECUTOR) 进行的;

3、THREAD_POOL_EXECUTOR.execute()

/*** 源码分析:THREAD_POOL_EXECUTOR.execute()* 说明:*     a. THREAD_POOL_EXECUTOR实际上是1个已配置好的可执行并行任务的线程池*     b. 调用THREAD_POOL_EXECUTOR.execute()实际上是调用线程池的execute()去执行具体耗时任务*     c. 而该耗时任务则是步骤2中初始化WorkerRunnable实例对象时复写的call()* 注:下面先看任务执行线程池的线程配置过程,看完后请回到步骤2中的源码分析call()*/// 参数设置//获得当前CPU的核心数private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//设置线程池的核心线程数2-4之间,但是取决于CPU核数private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));//设置线程池的最大线程数为 CPU核数*2+1private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;//设置线程池空闲线程存活时间30sprivate static final int KEEP_ALIVE_SECONDS = 30;//初始化线程工厂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());}};//初始化存储任务的队列为LinkedBlockingQueue 最大容量为128private static final BlockingQueue<Runnable> sPoolWorkQueue =new LinkedBlockingQueue<Runnable>(128);// 根据参数配置执行任务线程池,即 THREAD_POOL_EXECUTORpublic static final Executor THREAD_POOL_EXECUTOR;static {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,sPoolWorkQueue, sThreadFactory);// 设置核心线程池的 超时时间也为30sthreadPoolExecutor.allowCoreThreadTimeOut(true);THREAD_POOL_EXECUTOR = threadPoolExecutor;}4、call()/*** AsyncTask的构造函数*/public AsyncTask() {// 初始化WorkerRunnable变量 = 一个可存储参数的Callable对象mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {// 添加线程的调用标识mTaskInvoked.set(true); Result result = null;try {// 设置线程的优先级Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);// 执行异步操作 = 耗时操作// 即 我们使用过程中复写的耗时任务result = doInBackground(mParams);Binder.flushPendingCommands();} catch (Throwable tr) {mCancelled.set(true);// 若运行异常,设置取消的标志throw tr;} finally {// 把异步操作执行的结果发送到主线程// 从而更新UI ->>分析1postResult(result); }return result;}};}/*** postResult(result)*/private Result postResult(Result result) {@SuppressWarnings("unchecked")// 创建Handler对象 ->> 源自InternalHandler类Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));// 发送消息到Handler中message.sendToTarget();return result;}/*** InternalHandler类*/private static class InternalHandler extends Handler {// 构造函数public InternalHandler() {super(Looper.getMainLooper());// 获取的是主线程的Looper()// 故 AsyncTask的实例创建 & execute()必须在主线程使用}@Overridepublic void handleMessage(Message msg) {AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;switch (msg.what) {// 若收到的消息 = MESSAGE_POST_RESULT// 则通过finish() 将结果通过Handler传递到主线程case MESSAGE_POST_RESULT:result.mTask.finish(result.mData[0]); ->>分析3break;// 若收到的消息 = MESSAGE_POST_PROGRESS// 则回调onProgressUpdate()通知主线程更新进度的操作case MESSAGE_POST_PROGRESS:result.mTask.onProgressUpdate(result.mData);break;}}}/*** result.mTask.finish(result.mData[0])*/private void finish(Result result) {// 先判断是否调用了Cancelled()//  若调用了则执行我们复写的onCancelled()// 即 取消任务时的操作if (isCancelled()) {onCancelled(result);} else {// 若无调用Cancelled(),则执行我们复写的onPostExecute(result)// 即更新UI操作onPostExecute(result);}// 注:不管AsyncTask是否被取消,都会将AsyncTask的状态变更为:FINISHEDmStatus = Status.FINISHED;}
  • 任务线程池类(THREAD_POOL_EXECUTOR)实际上是1个已配置好的可执行并行任务的线程池;

  • 调用THREAD_POOL_EXECUTOR.execute()实际上是调用线-程池的execute()去执行具体耗时任务;

  • 而该耗时任务则是步骤2中初始化 WorkerRunnable实例对象时复写的call()内容;

  • 在call()方法里,先调用 我们复写的doInBackground(mParams)执行耗时操作;

  • 再调用postResult(result), 通过 InternalHandler 类 将任务消息传递到主线程;根据消息标识(MESSAGE_POST_RESULT)判断,最终通过finish()调用我们复写的onPostExecute(result),从而实现UI更新操作

  • AsyncTask的源码 分析完毕;

总结

  • 生命周期:AsyncTask不与任何组件绑定生命周期,所以在Activity/或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(boolean);

  • 内存泄漏:如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露;

  • 结果丢失:屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效;

多线程之 AsyncTask 使用详解和从源码中深入理解 AsyncTask 机制相关推荐

  1. android WebView详解,常见漏洞详解和安全源码(下)

    上篇博客主要分析了 WebView 的详细使用,这篇来分析 WebView 的常见漏洞和使用的坑.  上篇:android WebView详解,常见漏洞详解和安全源码(上)  转载请注明出处:http ...

  2. android WebView详解,常见漏洞详解和安全源码(上)

    这篇博客主要来介绍 WebView 的相关使用方法,常见的几个漏洞,开发中可能遇到的坑和最后解决相应漏洞的源码,以及针对该源码的解析.  由于博客内容长度,这次将分为上下两篇,上篇详解 WebView ...

  3. FPGA学习之路—接口(3)—SPI详解及Verilog源码分析

    FPGA学习之路--SPI详解及Verilog源码分析 概述 SPI = Serial Peripheral Interface,是串行外围设备接口,是一种高速,全双工,同步的通信总线. 优点 支持全 ...

  4. faster rcnn fpn_Faster-RCNN详解和torchvision源码解读(三):特征提取

    我们使用ResNet-50-FPN提取特征 model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True) ...

  5. 【Python】基金/股票 最大回撤率计算与绘图详解(附源码和数据)

    如果你想找的是求最大回撤的算法,请跳转:[Python] 使用动态规划求解最大回撤详解 [Python]基金/股票 最大回撤率计算与绘图详解(附源码和数据) 0. 起因 1. 大成沪深300指数A 5 ...

  6. 生成对抗网络入门详解及TensorFlow源码实现--深度学习笔记

    生成对抗网络入门详解及TensorFlow源码实现–深度学习笔记 一.生成对抗网络(GANs) 生成对抗网络是一种生成模型(Generative Model),其背后最基本的思想就是从训练库里获取很多 ...

  7. CNN入门详解及TensorFlow源码实现--深度学习笔记

    CNN入门详解及TensorFlow源码实现–深度学习笔记 ##一.卷积神经网络 ###1.简介 卷积神经网络是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出 ...

  8. EKF SLAM Matlab仿真实践详解(附源码)

    EKF SLAM Matlab仿真实践详解(附源码) 为提供更好的阅读体验,详细内容及源码请移步https://github.com/Nrusher/EKF_SLAM 或 https://gitee. ...

  9. aes js加密php解密实例,基于PHP和JS的AES相互加密解密方法详解(CryptoJS)_PHP_JS_AES源码...

    [实例简介] 基于PHP和JS的AES相互加密解密方法详解(CryptoJS)_PHP_JS_AES源码 [实例截图] [核心代码] 基于PHP和JS的AES相互加密解密方法详解(CryptoJS)_ ...

最新文章

  1. python编程语法-Python编程入门——基础语法详解(经典)
  2. java8 lambda 接口_Java8新特性之一:Lambda表达式
  3. 为了上班摸鱼,我用Python开发“BOSS来了”!
  4. 前端学习(2267)vue造轮子之添加icon
  5. 《信息安全系统设计基础》实验四报告
  6. c++ 未定义标识符string_Redis之String的数据结构
  7. 空降新书榜,霸占前三甲,还有什么是这些书做不到的?!
  8. 数学建模学习(24): 排队论模型完整详细讲解,数学与案例结合,lingo软件搭配,数学不好也能学会!
  9. SOP封装的后缀字母L M N都代表什么意思?
  10. Unity官方文档(英文)
  11. 大数据产品开发流程规范_大数据架构流程图
  12. python编写摇骰子游戏_python摇骰子猜大小的小游戏
  13. 操作系统----校招笔试面试常考内容总结
  14. 项目管理——时间、成本、范围的三重约束
  15. 对Autorun.inf类U盘病毒的攻防经验总结
  16. homelede软路由设置方法_二级lede软路由设置方法,lede设置软路由网卡
  17. 真实吐槽点评:华为nova8SE和华为nova7Pro区别-哪个更值得入手-参数对比
  18. 发现网站被劫持该怎么办?网站域名劫持的情况及解决办法
  19. 企业资金链断裂如何表现在现金流量表中?看净现金流量吗?
  20. 408计算机组成原理历年真题

热门文章

  1. shell常用命令高级部分
  2. 电脑桌面便签怎么复制单条便签内容?
  3. Linux下安装gradle
  4. python秒表模块_如何使用Python 实现秒表功能?
  5. Linux存储管理(3)
  6. ubuntu20.04 安装 坚果云,点击坚果云打不开,没有任何反应
  7. oracle考试复习题目整理
  8. linux ntp时间立即同步命令_Linux 时间同步 ntpd
  9. 【经验分享】项目经理如何击退被工作汇报支配的恐惧感?
  10. AtCoder Beginner Contest 190 A~D 题解