AsyncTask小酌

前言:最近的目标就是跳槽,跳槽完再搭个站,这样的话生活就充实一点点了吧。

今天说的AsyncTask也是项目中常常会用到的,面试中常常会问到的。可是我一般都不用这个的,但是新项目中也需要网络请求框架啊,而且asynctask这种不需要new thread和handler就能实现异步更新,我是非常赞赏的,因为很多网络框架其实都是基于这样的消息通知机制,把网络请求封装在底层,用户只需要重写回调接口的方法即可!

Enum枚举类

经过我长久以来积攒的经验,当分析源码的时候,最好先找一下类中的枚举常量,知道他们大概是代表什么意思,在什么情景下会使用到

指定任务的当前状态

在任务的生命周期,任务的每一个的状态只会被指定一次,如果任务还没有被执行,那么状态为PENDING,如果任务正在运行,那么指定为RUNNING,如果任务已经执行完毕了,那么状态指定为FINISHED。一个整体上的了解,AsyncTask有三个状态,分别为运行前,中,后。

AsyncTask

AsyncTask能够在UI线程使用,它不需要操作Thread和Handler就可以实现进行后台操作和在UI线程显示结果。

AsyncTask并不是一个通用的线程框架,而是作为一个在Thread和Handler之间的工具类。AsyncTask理论上用来执行少量的操作(最多花费几秒)。如果需要保持线程运行很长一段时间,强烈建议你使用java.util.concurrent提供的其他的API,比如Executor,ThreadPoolExecutor和FutureTask(有关线程池部分的内容)。

完成一次异步任务,我们在定义的时候可以根据需要定义三种类型的变量ParamsProgressResult,四个步骤onPreExecutedoInBackgroundonProgressUpdateonPostExecute即可。派生一个子类,这个子类必须重写doInBackground方法,通常还会重写的第二个方法是onPostExecute来展示运行的结果。

官方提供的用例

异步任务中的三种类型的参数分别如下:

Params:在任务执行之前,发送给任务的参数类型

Progress:后台线程计算的时候,输出的进度单元

Result:后台线程计算的结果的类型

哲学参数都是可选的,为了标记一个类型没有被使用,简单的使用Void类型即可,要是什么参数都不需要设定的话,那么使用<Void,Void,Void>即可。

AsyncTask创建——执行的过程:

AsyncTask的创建

AsyncTask是一个抽象类,所以要实例化必须要创建一个继承自AsyncTask的子类,我们在创建这个子类的时候就会指定各种参数的类型,在AsyncTask的构造函数中,就做了两样事情,一个是创建一个Callable对象mWorker,一个是根据mWorker创建一个FutureTask,记住,AsyncTask是一次性的买卖

关于设置线程优先级,setPriority是JDK提供的方法,而Process.setThreadPriority是Android的方法。我们都知道任何线程他们执行的都是Runnable方法,不管是继承Thread,或者是实现Runnable接口,实际上都是实现的run方法,而Callable我们可以理解成的是一个有返回值的run方法,这个Callable到最后也会适配成一个Runnable,适配的工作就是交由FutureTask来完成。

任务实体

FutureTask是一个管理Callable的类,有关Future相关的字样总是离不开Callable的,在AsyncTask的构造方法内,通过mWorker来创建了一个FutureTask对象

这里先简单介绍一下FutureTask,FutureTask实现了RunnableFuture接口,这个RunnableFuture接口继承了Runnable和Future接口,Future接口里面其实是一些针对Callback的操作,比如cancel,done,get等,用于获取call的结果,取消call等。而FutureTask内部有一个Callable<V>的成员变量callable

再看FutureTask的两个构造函数,可以从这两个构造函数中看出,FutureTask的构造其实就是要初始化callable对象,如果传入的是一个Runnable对象,就同Executors的callable方法适配成callable对象(Executors其实是一个封装好的工具类)

Callback参数
Runnable参数
result对象是返回值,因此task要负责对result进行计算

至此我们就获得一个FutureTask,它才是异步任务的大功臣,AsyncTask只是发言代表而已。AsyncTask还添加了一些类似于get,cancel等方法,其实是AsyncTask提供给外部,对FutureTask进行直接控制的方法。get方法分阻塞和超时非阻塞两种,一般使用情况不多。

AsyncTask的运行

当一个异步任务执行的时候,我们会调用execute这个方法,并且任务的执行过程会经历四个步骤:

以下四个方法都是需要默认空实现需要我们重写的方法。

1、onPreExecute(战前动员)

在任务执行之前,在UI线程中调用,这一步骤通常用来安装任务,比如显示一个进度条在用户界面,很多时候我们都没有重写这个方法的。

2、doInBackground

onPreExecute执行完毕之后就会调用doInBackground,而doInBackground是mWorker中的call回调中被调用,这一步被用来执行耗时的后台计算,AsyncTask的Params参数会传递到这一步骤,计算的结果必须在这一步被返回,当我们调用execute的时候,就会调用起这个方法,执行耗时操作了。

默认的任务管理器
自定义的任务池

当我们调用AsyncTask的execute时,就会往任务池里面添加一个runnable任务,并且自动执行任务池里的任务,执行完一个的时候执行下一个,正常情况下其实只会有一个任务,那就时构造函数内实现的mFuture对象

execute

3、onProgressUpdate

execute的时候,可以在doInBackground中主动调用publishProgress来输出一个或者多个进度值(Progresser类型值),这些值都可以在在onProgressUpdate这一步直接在UI线程通过进度条等方式显示出来。即使后台计算仍在运行,这个方法可以在UI进程中显示任何形式的进度。比如,可以通过移动一个进度条或者显示一些logs在文本域。

显示进度的方法

4、onPostExecute

在后台计算结束后,就会在UI线程调用onPostExecute,后台计算的结果会作为Result类型的参数传递到这一步。

在构造AsyncTask的时候,再mWorker中调用postResult来传递任务执行的结果,这个结果是最后的结果了。

容错操作:在FutureTask中方Callable执行完毕的时候调用done方法,在done方法里面当任务执行出错,超时什么的,任务还没执行就down了的话,就会调用起这个方法,要是postResult成功调用了,那么这个方法就不会被调起。反正就是无论如何都要执行postResult,代表这个任务已经结束了。

Handler消息机制

关于Handler消息机制,大家可以去查看我之前的Handler消息机制小酌。这么就不细说了

5、取消一个任务

可以在任意时间取消一个任务通过调用cancel(boolean state),调用这个方法将会导致后续调用isCanceled()来判断当前任务状态的时候返回true。

调用这个方法之后,任务将不会正常结束,onCanceled(Object)会代替onPostExecute被执行,在doInBackground方法返回的时候。为了确保任务可以尽可能快地取消,应该总是周期性地在doInBackground中检查isCanceled()的返回值,如果可能的话设置一个循环在里面。

例子中的用法一样

使用规则

为了使这个类能正常地使用和运行,有几个规则必须要遵守的:

AsyncTask类必须在UI线程中加载,在API 16中这是自动完成的,任务实例必须在UI线程创建,并且在UI线程调用execute方法,不要主动调用onPreExecute,doInBackground,onProgressUpdate,任务只能够被执行一次,如果试图执行第二次,那么就会抛出异常。

内存可观测

AsyncTask保证所有回调调用在这种方式下都是同步的,所以每一个操作都是安全的,不会出现多线程同时访问的问题,不需要指定为同步:

在构造器中或者在onPreExecute中设置成员变量,并且在doInBackground中引用他们。

在doInBackground中设置成员变量,并且在onProgressUpdate和onPostExecute中引用他们

执行的次序

AsyncTask第一次被介绍的时候,是被连续执行在一个单一后台线程中的。从API4开始,改成使用一个线程池来允许多任务同步执行。自从API 11开始,任务被执行在一个单一的线程来避免同步执行带来的普通应用错误,如果你真的需要同步执行,可以通过threadPoolExecutor调用executeOnExecutor(java.util.concurrent.Executor, Object[])来实现。

线程池部分

私有静态常量(外部不可直接访问)

CPU_COUNT:获取当前可用的cpu的计数。

CORE_POOL_SIZE:线程池中的核心线程数等于CPU总数加1。

MAXIMUN_POOL_SIZE:线程池中最大线程数等于CPU总数的两倍加1。

KEEP_ALIVE:线程执行完毕后是否维持线程。

以上的都是常量部分,用于设置默认的线程池实现。用于创建一个ThreadPoolExecutor对象,对于这些静态常量,同一个子类所创建的AsyncTask实例是共用这些对象的。就好像一开始说的,AsyncTask对象是一次性买卖。上面提到的任务池的作用就是当我们提交多个AsyncTask实例的时候,就会进入队列等待了。

AsyncTask

相对于Handler,AsyncTask反而没那么复杂了,当然了,要是加上了线程池部分的,就复杂多了啊,不过线程池也不是一时半刻可以理完了,毕竟有了Thread才有ThreadPool嘛

说好的进程通信呢...
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容