AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并主线程中更新UI,通过AsyncTask可以更加方便执行后台任务以及在主线程中访问UI,但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池

我们简单的模拟下载文件的案例来分析,我们创建自己的异步类继承AsyncTask

一、参数分析

通过源码,可以看到AsyncTask是个抽象的泛型类

  • Params:表示后台任务执行时的参数类型(对应例子中的URL),该参数会传给AysncTask的doInBackground()方法
  • Progress:表示后台任务的执行进度的参数类型(对应例子中的Integer),该参数会作为onProgressUpdate()方法的参数
  • Result:表示后台任务的返回结果的参数类型(对应例子中的Long),该参数会作为onPostExecute()方法的参数

注意这三个参数都是代表的类型,如果AsyncTask确实不需要传递具体的参数,那么这三个泛型参数可以用Void来代替

二、方法分析

常用的AsyncTask继承的方法有

  • onPreExecute():异步任务开启之前回调,在主线程中执行
  • doInBackground():执行异步任务,在线程池中执行
  • onProgressUpdate():当doInBackground中调用publishProgress时回调,在主线程中执行
  • onPostExecute():在异步任务执行之后回调,在主线程中执行
  • onCancelled():在异步任务被取消时回调

三、开启异步

在主Activity中通过点击按钮执行我们创建的异步任务,然后通过execute()方法执行异步任务

四、效果图

源码分析是基于API24的源码,我将会按照下面AsyncTask运行的过程来分析

一、主分支

  1. 首先,execute()方法,开启异步任务
  2. 接着,onPreExecute()方法,异步任务开启前
  3. 接着,doInBackground()方法,异步任务正在执行
  4. 最后,onPostExecute()方法,异步任务完成

二、次分支

  1. onProgressUpdate()方法,异步任务更新UI
  2. onCancelled()方法,异步任务取消

代码开始的地方,是在创建AsyncTask类之后执行的execute()方法

execute()方法会调用executeOnExecutor()方法

AsyncTask定义了一个mStatus变量,表示异步任务的运行状态,分别是PENDING、RUNNING、FINISHED,当只有PENDING状态时,AsyncTask才会执行,这样也就保证了AsyncTask只会被执行一次

继续往下执行,mStatus会被标记为RUNNING,接着执行,onPreExecute(),将参数赋值给mWorker,然后还有execute(mFuture)

这里的mWorker和mFuture究竟是什么,我们往下追踪,来到AsyncTask的构造函数中,可以找到这两个的初始化

一、分析mWorker

mWorker是一个WorkerRunnable对象,跟踪WorkerRunnable

实际上,WorkerRunnable是AsyncTask的一个抽象内部类,实现了Callable接口

二、分析mFuture

mFuture是一个FutureTask对象,跟踪FutureTask

实际上,FutureTask是java.util.concurrent包下的一个类,参数是个callable,并且将它赋值给FutureTask类中的callable

三、回过头来看

回到我们AsyncTask初始化mFuture,这里的参数是mWorker也就不奇怪了,因为mWorker就是一个callable,我们在上面赋值给FutureTask类中的callable就是这个mWorker

而关于mWorker和mFuture的初始化早在我们Activity中初始化好了,因为构造函数是跟AsyncTask类的创建而执行的


知道了mWorker和mFuture是什么后,我们回到原来的executeOnExecutor()方法,在这里将mWorker的参数传过去后,就开始用线程池execute这个mFuture

一、分析exec

exec是通过executeOnExecutor()参数传进来的

也就是我们execute()方法传过来的

这里可以看到exec就是这个sDefaultExecutor

二、分析sDefaultExecutor

我们跟踪这个sDefaultExecutor,截取有关它的代码

从SerialExecutor可以发现,exec.execute(mFuture)就是在调用SerialExecutor类的execute(final Runnable r)方法,这里的参数r就是mFuture

继续往下走,SerialExecutor的execute()方法会将r封装成Runnable,并添加到mTasks任务队列中

继续往下走,如果这时候没有正在活动的AsyncTask任务,那么就会调用SerialExecutor的scheduleNext()方法,来执行下一个AsyncTask任务

继续往下走,通过mTasks.poll()取出,将封装在mTask的Runnable交给mActive,最后真正执行的这个mActive的是THREAD_POOL_EXECUTOR,即执行的这个mActive,也就是包装在Runnable里面的mFuture

mFuture被执行了,也就会执行它的run()方法


我们跟踪到mFuture的run()方法中,切换到FutureTask类

这一段代码其实就是将之前在mFutrue创建对象时候传进来的mWorker交给c

然后再调用c的call()方法,也就是mWorker的call()方法


代码又重新的定位到了mWorker类的call()方法

可以发现,这里就调用了我们的doInBackground()方法,最后还返回postResult(),我们跟踪这个postResult()方法

观察代码,首先是getHandler(),它是一个单例,返回sHandler

也就是说postResult方法会通过sHandler发送一个MESSAGE_POST_RESULT的消息,这个时候我们追踪到sHandler

可以发现,sHandler收到MESSAGE_POST_PROGRESS消息后会调用result.mTask.finish(result.mData[0]),那么我们还必须知道result是个什么东西

result是AsyncTask的内部类,实际上就是个实体类,用来存储变量的

result.mTask也就是AsyncTask,最后调用result.mTask.finish(result.mData[0]),即AsyncTask的finish()方法,我们跟踪到finish()方法

这里判断AsyncTask是否已经取消,如果不取消就执行我们的onPostExecute(),最后将状态设置为FINISHED,整一个AsyncTask的方法都执行完了,我们只需要继承AsyncTask实现其中的方法就可以按分析的顺序往下执行了

在AsyncTask中的finish()方法,我们可以看到onCancelled()方法跟onPostExecute()一起的,雅思高频词汇只要isCancelled()的值为true,就执行onCancelled()方法

我们代码跟踪isCancelled()方法

发现是在mCancelled中获取的,那我们就必须知道这个mCancelled是什么,代码跟踪到mCancelled

mCancelled实际上就是个Boolean对象,那我们搜索它是在哪个时候设置的

可以发现,只要我们在AsyncTask类中调用这个方法即可停止异步任务


而onProgressUpdate()方法,是在sHandler中执行,sHandler收到MESSAGE_POST_PROGRESS消息后,执行,我们搜索MESSAGE_POST_PROGRESS在什么时候发送的

可以发现,只要我们在AsyncTask类中调用publishProgress()方法即可执行onProgressUpdate()方法

AsyncTask使用与源码分析就到这里结束了,如果是初学者,这可能会是比较难的分析,如果是想往中高级进阶的朋友,了解AsyncTask的原理是必须的。随着时代的发展,流行的异步框架RxJava的出现已经可以说是替代了这个AsyncTask,为了跟进时代的发展,我也得抽取时间来学习了,废话就不说了,还是节省点时间来学习吧

Android进阶——多线程系列之异步任务AsyncTask的使用与源码分析的更多相关文章

  1. Android进阶——多线程系列之Thread、Runnable、Callable、Future、FutureTask

    多线程一直是初学者最抵触的东西,如果你想进阶的话,那必须闯过这道难关,特别是多线程中Thread.Runnable.Callable.Future.FutureTask这几个类往往是初学者容易搞混的. ...

  2. 【转载】Android异步消息处理机制详解及源码分析

    PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...

  3. Android查缺补漏(View篇)--事件分发机制源码分析

    在上一篇博文中分析了事件分发的流程及规则,本篇会从源码的角度更进一步理解事件分发机制的原理,如果对事件分发规则还不太清楚的童鞋,建议先看一下上一篇博文 <Android查缺补漏(View篇)-- ...

  4. dubbo同步调用、异步调用和是否返回结果源码分析和实例

    0. dubbo同步调用.异步调用和是否返回结果配置 (1)dubbo默认为同步调用,并且有返回结果. (2)dubbo异步调用配置,设置 async="true",异步调用可以提 ...

  5. 多线程高并发编程(12) -- 阻塞算法实现ArrayBlockingQueue源码分析(1)

    一.前言 前文探究了非阻塞算法的实现ConcurrentLinkedQueue安全队列,也说明了阻塞算法实现的两种方式,使用一把锁(出队和入队同一把锁ArrayBlockingQueue)和两把锁(出 ...

  6. Android 4.4以上使用HttpURLConnection底层使用OkHttp实现的源码分析

    研究了一下HttpURLConnection的源码: 在使用的时候都是通过URL.openConnection()来获取HttpURLConnection对象,然后调用其connect方法进行链接,所 ...

  7. Android布局优化之ViewStub、include、merge使用与源码分析

    在开发中UI布局是我们都会遇到的问题,随着UI越来越多,布局的重复性.复杂度也会随之增长.Android官方给了几个优化的方法,但是网络上的资料基本上都是对官方资料的翻译,这些资料都特别的简单,经常会 ...

  8. 多线程高并发编程(11) -- 非阻塞队列ConcurrentLinkedQueue源码分析

    一.背景 要实现对队列的安全访问,有两种方式:阻塞算法和非阻塞算法.阻塞算法的实现是使用一把锁(出队和入队同一把锁ArrayBlockingQueue)和两把锁(出队和入队各一把锁LinkedBloc ...

  9. Android进阶——多线程系列之wait、notify、sleep、join、yield、synchronized关键字、ReentrantLock锁

    多线程一直是初学者最困惑的地方,每次看到一篇文章,觉得很有难度,就马上叉掉,不看了,我以前也是这样过来的.后来,我发现这样的态度不行,知难而退,永远进步不了.于是,我狠下心来看完别人的博客,尽管很难但 ...

随机推荐

  1. Matplotlib 入门

    章节 Matplotlib 安装 Matplotlib 入门 Matplotlib 基本概念 Matplotlib 图形绘制 Matplotlib 多个图形 Matplotlib 其他类型图形 Mat ...

  2. poj 3069 Saruman's Army 贪心模拟

    Saruman's Army Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 18794   Accepted: 9222 D ...

  3. python中logging的使用

    什么是日志: 日志是一种可以追踪某些软件运行时所发生事件的方法 软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情 一个事件可以用一个可包含可选变量数据的消息来描述 此外,事件也 ...

  4. flask-Bootstrap Jinja2 原生 模板 和 jumpserver 模板

    #模板 {% block doc -%} <!DOCTYPE html> <html{% block html_attribs %}{% endblock html_attribs ...

  5. vue - @click 传参删除

    <template>   <div id="app">      <div v-for="todo in  todos" :key ...

  6. STM32F103 USB虚拟串口 驱动例程移植

    1)驱动下载及安装.目前ST公司支持WIN7版本号为:VCP_V1.3.1_Setup.exe (在官网上搜索stsw-stm32102即是了):先安装驱动后再插入USB不然安装不成功. 2)固件下载 ...

  7. Vmware 和 VisualSVN-Server端口冲突

    安装 VisualSVN-Server 时,发现他和 Vmware   在端口  443 冲突: 先把本地自启动的 Vmware 全部停止,并改成手工启动服务: 这样可以节省资源,再安装 svn服务时 ...

  8. 07.Delphi接口的生命周期

    在Delphi的接口中,是不需要释放的,调用完之后,接口的生命周期就结束了,如下面的例子 unit mtReaper; interface type // 定义一个接口 IBase = interfa ...

  9. ej3-1优先使用静态工厂方法而非构造函数来创建对象

    背景 很早之前就已经自己翻译了,先简单的贴出来,并做一下回顾. 条款1 优先使用静态工厂方法而非构造函数来创建对象 允许客户端创建一个实例的传统方法是:提供一个公共构造函数:有另外一个必须成为每个程序 ...

  10. 吴裕雄--天生自然java开发常用类库学习笔记:属性类Properties

    import java.util.Properties; public class PropertiesDemo01{ public static void main(String args[]){ ...