前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AsyncTask? AsyncTask串行and并行?

AsyncTask? AsyncTask串行and并行?

作者头像
陈宇明
发布2020-12-15 14:48:14
1K0
发布2020-12-15 14:48:14
举报
文章被收录于专栏:设计模式

作者博客

http://www.cherylgood.cn/

目录

  1. 前言
  2. AsyncTask知识补充
  3. AsyncTask使用注意事项
  4. 以日常使用为起点分析AsyncTask的源码
  5. 我想让AsyncTask并行执行,可以么?
  6. 总结

1

前言

AsyncTask,相信你不会陌生,也许你很幸运,早已了解了AsyncTask这个家伙挖的坑,也许你已经被坑过了,也许你没坑了,然而还没有发觉!

本次笔者将带大家一起来看下AsyncTask这个坑是如何挖出来的。

啥也不说啦,我们先来段代码看看。

1、首先创建一个AsyncTask类

2、假设你这样运行

3、你觉得会发生什么呢?

  • 在Android3.0之前执行的时间是一样的,在Android3.0之后每个执行时间相差2秒;
  • android3.0虽然已经基本不用适配了,但是我们了解一下还是不错的哦。

2

AsyncTask知识补充

(如果你对AsyncTask已经很熟了,可以跳过该章节)

  • 在讲解AsyncTask的坑之前,我们先了解下AsyncTask的源码,有助于我们理解为什么他是个坑,我们应该如何处理这个坑,当然,你也可以直接跳到下一章节。
  • 系统为了帮助开发者简化线程的切换问题,很人性化的提供了轻量级的AsyncTask。AsyncTask的发展经历了几次的修改,进而为我们挖下了一个很容易被忽略掉的坑。
  • AsyncTask作为一种轻量级的异步任务类,在Android开发过程中颇受开发者的喜爱。使用AsyncTaskNike 以在线程池中之行后台任务,并把之行的进度和最终结果传递给UI线程做进一步的操作。
  • 本质上,AsyncTask也是封装了Thread和Handler,前面我们在《Android 开发之Handler的前世今生》中通过Handler的源码分析并学习了Handler的实现流程。有需要的小伙伴可以点击一下,有不好的地方欢迎留言哦。
  • 大家都知道AsyncTask是一个抽象类,一般我们继承AsyncTask来创建我们的AsyncTask,而创建的时候我们需要提供三个泛型参数(~~||我怀疑我是不是在瞎逼逼了),这三个泛型参数跟AsyncTask关系不浅哦。
  1. Params:表示我们在调用execute方法时传递的参数类型;
  2. progress:表示后台任务的执行进度类型;
  3. Result:表示后台任务的返回结果类型。

(不需要的的数据可以设置为Void哦)

  • AsyncTask也为我们提供了如下方法:
  1. onPreExecute():该方法会在主线程中被调用,你可以在该方法内部编写一些如显示加载动画的逻辑代码;
  2. doInBackground:该方法在子线程中执行,一般在该方法中编写需要在子线程中执行的操作。如果你有更新进度的需求,可以通过调用publishProgress方法,该方法会调用onProgresssUpdate方法,调用publishProgress方法时需要传递一个进度值,该值最终会在onProgresssUpdate中调用,为什么要这样做呢?因为onProgresssUpdate实在主线程中执行的,只有主线程才可以更新UI哦。当然执行doInBackground时你需要返回一个结果集。该结果集最终在onPostExecute中获取。同样的原因,onPostExecute是在主线程中运行的;(线程总是切换来切换去,真烦是不是?)
  3. onProgresssUpdate:上面讲过了,这里就略过了;
  4. onPostExecute:同样onPostExecute也是;
  5. onCancelled:在主线程中执行。当异步任务被取消时被毁掉。此时onPostExecute则不会被调用了哦。

3

AsyncTask使用注意事项

  1. AsyncTask类第一次加载必须在主线程中加载(这个不需要我们关系,ActivityThread已经帮我们做了);
  2. AsyncTask对象的创建代码必须在主线程中调用;
  3. execute方法必须在主线程中调用;
  4. onPreExecute、onPostExecute、doInBackground、onProgressUpdate 四个方法不能在程序中直接调用;
  5. 一个AsyncTask实例只能调用一次execute方法;
  6. AsyncTask执行execute方法时在Android1.6之前串行;Android1.6之后并行;Android3.0后串行。

4

以日常使用为起点分析AsyncTask的源码

OK,假设你使用AsyncTask时都是使用execute方法来调用,那么我们就从他入手吧!

我们进入execute的方法体看下,里面做了什么呢?

我先我们看到一个@MainThread的注解。这是什么东东呢?我没用过呢。

其实它是google提供的一个注解,标注我们的这个方法必须在主线程中调用,如果不是的话,AS就会红色提示我们哦。

然后我们可以看到,execute里面其实是执行了executeOnExecutor方法,他传入了两个参数:

1、sDefaultExecutor(这是干什么的呢?)

2、params 从execute传递下来的可变参数。

OK,重点就是sDefaultExecutor了,从名字上看sDefaultExecutor是个磨人的执行器的意思,但我们是很较真的人,所以必须看下sDefaultExecutor是个什么东东,所以就逗逼逗逼的去找相关代码了。

OK,从其定义的地方可以看到,sDefaultExecutor是一个Executor类型的实例,通过SerialExecutor()获得。我们下看SerialExecutor()里面都做了什么?

从上面代码可以看到,里面有一个mTasks的数组队列。当我们调用Execute的时候就会在mTasks队列中插入一个runnable实例,也就是说,mTasks里面存放的是线程,可能你会很奇怪,首先这里public synchronized void execute(final Runnable r)传如了一个runnable,然后这里mTasks.offer(new Runnable() 又创建一个新的runnable入队列。

OK,更详细的分析我放到了代码中。

OK,我们继续回到executeOnExecutor方法。

小结:

通过上面代码,我们知道,在执行AsyncTask的execute方法时,我们的线程默认是放入一个ArrayQueue队列中串行的执行的。也就是sDefaultExecutor是一个串行的执行器,所以我们的AsyncTask默认是串行自行的。

OK,我们来进一步分析下AsyncTask的执行过程,首先我们看下AsyncTask的构造方法都做了什么。

①:创建一个WorkerRunnable,注意第二个泛型参数Result,这个就是我们继承AsyncTask时传递的第三个参数哦,然后里面看到调用了result = doInBackground(mParams);最终都会调用postResult(result);方法。

②:创建一个FutureTask的对象,mFuture就是我们前面executeOnExecutor方能发中最重调用exec.execute(mFuture);传入的参数哦,说明mFuture是个runnable类型的实例。然后把mWorker传入创建,说明runable执行的是mWorker里面的工作,yes,所以我们的doInBackground方法是在runable线程中执行的。

OK,我们之前只说了我们的代码是如何放入子线程中的,但是并没有看见启动子线程的代码啊?我们可以看奥,他最终都调用的postResult方法。

OK,可以看到,里面是通过我们熟悉的Handler来进行实现的哦。我们看他他传入了MESSAGE_POST_RESULT和AsyncTaskResult,this就是我们的AsyncTask了,后面应该是在handler中调用了AsyncTask的某个方法,我们看下getHandler()。

OK,里面是一个InternalHandler实例。而InternalHandler是这样的。

OK,里面看到了MESSAGE_POST_RESULT对应的是finish方法。也就是我们的AsyncTask的finish方法。

可以看到,里面最终会调用onCancelled(result);或者onPostExecute(result);

小结:

创建 AsyncTask是会创建mWorker和mFuture,mFuture是一个runable类型的对象,最终会在我们执行execute-》executeOnExecutor时,传入执行队列中等待执行。在传如前先调用了onPreExecute()方法,在mFuture被执行的时候,会回调mWorker的call方法,call方法里会调用doInBackground方法,获得doInBackground的执行结果后调用postResult方法,postResult内部通过handler切换线程,最终调用我们的finish方法,finish里面会调用onCancelled(result)或者onPostExecute(result);中的一个。这样我们的AsyncTask的一个关键流程就走完了。

5

我想让AsyncTask并行执行,可以么?

当然可以,我们前面分析了,串行还是并行,关键是执行器。因为默认传入的是sDefaultExecutor,sDefaultExecutor是个串行的执行器,所以我们传入一个并行的执行器,是不是就可以了呢?

我们可以看到AsyncTask也提供了一个THREAD_POOL_EXECUTOR,根据注解,使用它可以让execute 并行工作

/** * An {@link Executor} that can be used to execute tasks in parallel. */public static final Executor THREAD_POOL_EXECUTOR;

看到这里我有点郁闷了,前面在分析SerialExecutor是,里面不也是将runable让入THREAD_POOL_EXECUTOR中执行么?

SerialExecutor为什么是串行呢?虽然也是放入THREAD_POOL_EXECUTOR中,但是他是从队列中取出来放入的哦,而且一次只取出一个,执行完再取第二个。

6

总结

通过本次学习,我们了解了AsyncTask从创建到执行返回结果的工作流程,也明白了为什么默认时串行执行的。当然,改为并行也是非常简单的,你可以自己自定义执行器,自己来控制,但是一般都不需要的( ̄∇ ̄)。通过本次学习,相信在使用AsyncTask时,你会更加踏实。厚积而薄发!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-07-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码个蛋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档