Android耗时任务处理方案--AsyncTask

来源:互联网 发布:c语言数组定义不加数字 编辑:程序博客网 时间:2024/05/20 19:52

在android应用中,每一个应用都对应一个进程,而应用的进程默认情况下只会开启一个线程即主线程。所有的操作都发生在主线程(或者称为UI线程)。
主线程的生死存亡是和进程一致的。
运行在UI线程中的操作主要有System Event, Input Event, Application, Service, Alarm, UI Drawing.这些类的代码都会执行在UI线程中。而且在这类操作中写的所有代码,包括自己写的代码也都会在UI线程执行。所以,可以看出UI线程真的比较繁忙。
假如你给一个Button 上一个监听器,他的功能是执行一个耗时的操作(比如下载),而你写的所有关于下载的代码都放在onClick方法中。这就会出现对UI线程的阻塞,而且这个阻塞会让UI线程中其他所有的工作都等待他执行完再进行!简单一点说就是:运行在UI线程中的操作System Event, Input Event, Application, Service, Alarm, UI Drawing都要会等待着!想一想,下载过程中如果用户戳你的屏幕,点一个输入框都不会有任何反应,如果有一个转菊花的动画,那朵菊花也会卡住,就是因为你的代码太耗时了。严重的情况下应用就会直接崩溃掉。这对于用户来说是一件非常灾难性的事情。
非常重要的一点就是UI线程还负责UI 渲染绘制。就比如说转菊花这个动画,菊花之所以能转,就是因为主线程每16ms就绘制一次屏幕。为了保证UI界面的流畅,就必须把任何UI线程的代码执行操作耗时限制在16ms以内。否则菊花就会卡住(遗漏了很多帧画面的绘制)。
如此严苛的代码执行16ms耗时限制肯定不能完成多数的耗时操作,哪怕是解析Json的工作也是耗时大概百ms的。更别提下载这样相当耗时的代码操作了。为了解决这个问题,所以多线程诞生了。
耗时的任务我们开一个子线程,子线程去执行耗时的代码操作,做完主线程交给的任务,结束自己这个线程。这样主线程就可以继续16ms就绘制一次UI,因此菊花就会正常转动了。
那么在android中如何开线程做耗时任务呢?Android框架给我们提供了这样4个方案:
1.AsyncTask
AysncTask是一个最常用的耗时任务异步处理方案。他的优势在于可以轻松完成耗时任务和UI线程之间的切换。
2.HandlerThread
有些任务并不是一定与UI发生关系,当需要一个专门用作API回调的工作线程时,HT很适合
3.ThreadPool
ThreadPool适合做一些频繁细小的耗时任务,线程池运行着许多并行的线程。所以对于频繁的小的工作线程池执行的更快。
4.IntentService
IntentService适合做后台任务,尤其是UI线程的有些Intent并不适合交给主线程中的任何组件处理。交给这个天然拥有HandlerThread的IntentService去后台执行这个Intent的任务反而更合适。

AsyncTask处理耗时任务

AsyncTask处理耗时任务也是基于子线程来处理的。
普通的子线程就做三件事:1.开始线程 2.执行线程中的任务代码 3.把自己这个线程结束掉。这样的线程功能是很有限的。如果你想让一个线程做完第一个任务以后先别结束,等着连续不断的接新任务代码接着执行怎么办?反正普通的线程就是简单的三件事。为了能够不断的接任务代码就需要对普通的线程加一点东西。那就是不断地给这个线程喂任务。这就是Looper以及Handler要做的事情。这样这个线程才能保持继续工作。但是不断喂得这些任务需要排队执行,那么就需要一个能够管理要执行的任务的东西。这就是MessageQueue要做的事情。MQ保证任务有序的交给线程去执行。同时任务从哪里来呢,还需要有一个线程去产生任务然后放进MQ里面。

AysncTask是利用Handler来实现耗时任务子线程与UI线程的数据传递的。既然本质上是一样的,那就先了解一下Handler是怎么运作的。

这里写图片描述

想想一下这个场景:你有一个耗时的任务,然后要改变UI线程(UI线程本身就是一个HandlerThread)的某个UI参数。这时候就要开一个线程(OtherThread)去做这件耗时的事情,但是完成之后又不能在该线程去直接改变UI。那就要把执行完成后的返回值做成一个Message交给Handler(这个Handler是属于HandlerThread的)发到MQ(这个MQ也是属于HandlerThread的)中。(当然也不一定是非要是一个Message,做成一个Intent,Runnable对象交给Handler发到MQ中也是没问题的,看具体自己的目的)。之后MQ会把发过来的Message交给HandlerThread处理。
当然Handler MQ Looper机制之所以存在就是因为很多时候,我们程序员有太多的耗时任务要做,然后改变UI线程的变量。如果有数十个线程想要改变同一个UI变量怎么办(注意UI线程是线程不安全的,也就是说UI线程的UI控件是不上锁的)?所以就要有MQ来保证这件事有序的进行下去。
AysncTask也是同样的内部机制。

AysncTask导致的内存泄漏问题

AsyncTask在作为Activity的内部类使用的时候很容易产生内存泄漏的问题。在使用过程中可能有这种场景:旋转屏幕Activity被destroy掉。但是内部的AsyncTask的任务还没有执行完。而内部类天然持有外部包裹类的引用。所以Activity对象占用的内存空间就不会被GC回收,直到AT自己的任务完成了销毁的自己,那么他持有的Activity的引用才会为null.泄漏一个Activity的引用。内存可能对用户影响不大。但是当连续不断的翻转手机,就会有大量的内存被不再需要的Activity对象所占据,直到内存被消耗光应用崩溃。或者AT完成了任务,想要去更新自己外部包裹类Activity的UI时,发现他已经destory掉了,导致应用崩溃。所以这是一个很严重的问题。
比较常用的办法就是把AT做成static的

2 0