版权声明:本文为博主原创文章,未经博主允许不得转载。
问题的由来
最近老看见有人问Android中的Thread与AsyncTask的区别,因此研究了一下,总结如下。
第一,如果真心想弄清楚这两个的具体用法,最好的方法是把Message、MessageQueue、Looper、Handler、HandlerThread和AsyncTask这六个类的源代码全部看懂。
第二,如果觉得困难,那么就来看看我的这篇总结文章吧。
Handler类,可以理解为消息/任务处理者,Handler类是为了与其他几个类一起完成Android的消息循环处理过程,是必不可少的一个类。它主要有两个用途:一是为了在将来某个时间点处理一个消息或者执行一个任务;二是将一个任务放入队列,以便它可以在另外的线程中执行。
AsyncTask类,可以理解为异步任务执行者;这个类的设计目的很明确,就是为了“执行一个较为耗时的异步任务(最多几秒钟),然后更新界面”。这种需求本可以使用Handler和Thread来实现,但由于编码较为复杂,因此Android提供了AsyncTask类。
几个规则
正式开始讨论Handler和AsyncTask之前,有几个Android的规则要再强调一次:
- 只能在UI线程中访问界面;
- UI线程被阻塞(大概5秒钟)后会导致ANR(Application Not Responding)错误。
因此我们要使用Handler来创建消息循环,或者使用AsyncTask来创建异步任务来进行操作。
Handler类的使用场景
消息循环三兄弟Handler、Looper和HandlerThread
Handler用来和Looper、HandlerThread一起建立一个具有消息循环的子线程。如果这三个类中有一个你不理解,那么你三个都不会理解,也不会理解Android的消息循环与处理机制。
Looper环形使者(顺便说一下这个电影很好看):Looper中带有一个MessageQueue,即消息队列,Looper负责轮询此消息队列,将消息取出后交由Handler来处理。
HandlerThread消息循环线程:即Handler执行的线程,此线程大部分时间都在运行Looper.loop()方法,即消息轮询方法;它会通过getLooper方法返回一个Looper对象,Handler需要使用此对象作为参数创建对象。
Handler处理者:负责接收消息、发送消息(sendMessage等方法)和处理消息(handleMessage方法),我们需要重载handleMessage方法来处理各种消息。
一个具有完整消息循环的线程必然包括Looper、Handler和HandlerThread对象(或者你自己用Thread类来写一个线程),其中Looper对象内部还包含一个MessageQueue对象,我们这样来建立一个消息循环子线程。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
记住,此处HandlerThread类不是为了运行一段自定义的代码来完成某个任务,而是为了让把消息循环运行起来,即运行Looper.loop()方法。
记住几个原则:
HandlerThread中创建了Looper对象,可以使用getLooper方法来得到这个Looper对象;
使用该Looper对象作为参数来创建Handler,才能使得此Handler能够处理消息;如果Handler对象创建时不带有对象,那么Handler会自动获取当前线程关联的Looper对象。因此若Handler是在主线程(即UI线程)中创建的,则Handler就会处理UI线程中的消息队列;如果Handler是在其他线程(此线程未创建Looper对象)中创建的,则此Handler不能处理消息。
因此,若我们不使用HandlerThread,而是直接使用Thread类,则代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
我们会发现上面的代码例子中不能得到那个mHandler,因此收发消息时没有Handler对象可以引用,若对多线程的并发很熟悉的人可以扩展上面的代码,安全的取得该Handler对象,否则还是直接使用HandlerThread为好。
其实消息循环是五兄弟,还要加上消息(Message类)和消息队列(MessageQueue类),不过由于Message较为简单,而MessageQueue又被隐藏得很好,几乎不会被普通用户见到,因此就省略不介绍了,可以自己去看相关文档。
在主线程中使用Handler
好了,相信很多不熟悉的人看上面的消息循环三/五兄弟已经看晕了,会问“为什么我用了好久的Handler,却根本就没有见过Looper和HandlerThread呢?”
其实,那是因为绝大部分时间,你只在主线程(也就是UI线程)中使用Handler,此时Handler会默认使用主线程的Looper和Thread。可以这样理解,普通的Android应用一旦运行起来,就会创建主线程和一个主Looper,它们默默的处理着界面消息,这时你new了一个Handler并重载了它的handMessage方法,你的这个Handler对象就加入到消息的处理中来了。
具体的用法,请看我的一篇blog:Android中的消息处理实例与分析
http://blog.csdn.net/logicteamleader/article/details/46591499
在子线程使用Handler
在子线程中使用Handler则比主线程复杂,因此此时就必须使用Looper和Thread/HandlerThread类了,例子仍可以参考上一篇blog。
AsycnTask怎么来的?
好了,终于说到AsycnTask了。前面已经说过,它是一个辅助类,就是为了将Handler、Thread等封装为一个异步执行框架,供Android Coder可以方便的使用。其主要目的是为了“在其他线程中执行一个耗时操作,并随时报告执行进度给UI线程,执行完成后将结果报告给UI线程”。
AsyncTask使用方法
AsyncTask的使用方法其实Android developer中已经说得非常清楚了,我自认不能比他说得更好,因此择重点翻译一次:
AsyncTask使用时必须作为基类被扩展,子类至少重载一个方法doInBackground,另一个方法onPostExecute也经常被重载,代码例子如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
AsyncTask含有三个泛型参数:
Params, 异步任务执行所需的参数类型;
Progress, 异步任务执行进度的类型;
Result, 异步任务执行结果的类型。
这三个参数不必全部使用,不使用的参数置为Void即可,例如:
- 1
AsyncTask的四个重要方法:
当一个异步任务被执行时,要经历四步:
onPreExecute(),在UI线程中执行,它会在异步任务开始前执行,一般用来设置任务参数;
doInBackground, 最重要的方法,在子线程中执行(事实上,只有它在子线程中执行,其他方法都在UI线程中执行)。当onPreExecute结束后,本方法立刻执行,它用来进行后台的耗时计算,异步任务的参数会被传给它,执行完成的结果会被送给第四步;执行途中,它还可以调用publishProgress 方法来通知UI线程当前执行的进度;
onProgressUpdate, 当publishProgress 被调用后,它在UI线程中执行,刷新任务进度,一般用来刷新进度条等UI部件;
onPostExecute, 当后台的异步任务完成后,它会在UI线程中被调用,并获取异步任务执行完成的结果。
小结
通过上面的分析,我们可以看到,其实Handler和AsyncTask的关系不太大,可以说不是同一个层面上的东西。只不过,它们有着一些共同的目的,就是都可以用来异步执行一些代码,避免阻塞UI线程。另外,AsyncTask实际上是使用Handler和线程池技术来实现的。
要想真正的了解它们之间的异同,还是那句话:RTFSC – Read The Fucking Source Code。