本文共 4597 字,大约阅读时间需要 15 分钟。
在消息机制中Handler扮演着举足轻重的作用,(AsnyTask其实也是对Handler+Thread做了一层封装),ui线程超过5s就会报出ANR,一般耗时操作操作需要放在子线程中处理,这时候Handler就可以大展身手,Handler主要用来处理完耗时操作将访问UI的工作切换到主线程去。
1、原理篇
简单概括一下:
Handler创建时会采用当前线程的Looper来构建内部的消息循环系统如果当前线程没有Looper那么就会报错,因此使用时主要为当前线程创建Looper即可或者在一个有Looper的线程中创建Handler也可以,在我们的UI线程中系统已经在一开始就为我们创建好了,自定义的子线程如何创建后面会说。
首先需要搞定几个概念
【MessageQueue】是一种,见名知义,就是一个消息队列(注意不是数据结构的队列,其实是个单链表),存放消息的地方。每一个线程最多只可以拥有一个MessageQueue数据结构。创建一个线程的时候,并不会自动创建其MessageQueue。通常使用一个Looper对象对该线程的MessageQueue进行管理。主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个Message Queue。其他非主线程,不会自动创建Looper,要需要的时候,通过调用prepare函数来实现。
示例:
俩行代码,该线程就具有自己的Looper啦
【Message】消息对象
MessageQueue里面可以存放Message的消息一个Message Queue中包含多个Message。Message实例对象的取得,通常使用Message类里的静态方法obtain(),该方法有多个重载版本可供选择;它的创建并不一定是直接创建一个新的实例,而是先从Message Pool(消息池)中看有没有可用的Message实例,存在则直接取出返回这个实例。
源码中可以看出如果Message Pool中没有可用的Message实例(poolsize=50),则才用给定的参数创建一个Message对象。调用removeMessages()时,将Message从Message Queue中删除,同时放入到Message Pool中。除了上面这种方式,也可以通过Handler对象的obtainMessage()获取一个Message实例。
通常Message Queue只有俩种单链表操作 插入(对应方法enqueueMessage)和读取(next)
【Looper】
是MessageQueue的管理者。每一个MessageQueue都不能脱离Looper而存在,Looper对象的创建是通过prepare函数来实现的。同时每一个Looper对象和一个线程关联。通过调用Looper.myLooper()可以获得当前线程的Looper对象创建一个Looper对象时,会同时创建一个MessageQueue对象。除了主线程有默认的Looper,其他线程默认是没有MessageQueue对象的,所以,不能接受Message。如需要接受,自己定义一个Looper对象(通过prepare函数),这样该线程就有了自己的Looper对象和MessageQueue数据结构了。Looper从MessageQueue中取出Message然后,交由Handler的handleMessage进行处理。处理完成后,调用Message.recycle()将其放入Message Pool中。
Looper扮演着消息循环角色,一有消息就处理,否则就阻塞在那里。
小结一下:
当Handler创建完毕后,内部的Looper以及MessageQueue就可以和Handler一起协同工作了,然后通过Handler的post方法将一个Runnable放到Looper中或者通过send方法发送一个消息,同样也是放到Looper中处理(post最终还是通过send方法来完成的),send然后调用enqueueMessage来插入到消息链表中,Looper发现有新消息来到就会处理这个消息,最终Runnable或者Handler的Messager方法就会被调用,要注意looper是运行在handler所在的线程中,这样handler中的业务逻辑就被切换到创建Handler的逻辑中去了
关于更加详细的Handler分析可以参看
2、使用篇
【handler】
对于Handler来说有两种主要的方式:
1. 计划好消息和Runnable将来的某一个时间点来执行它 2. 从一个不同的线程中执行Handler的入队操作。分发消息由下面的几个方法完成:
1) post(Runnable), 2) postAtTime(Runnable, long), 3) postDelayed(Runnable, long),4) sendEmptyMessage(int),
5) sendMessage(Message), 6) sendMessageAtTime(Message, long), 7) sendMessageDelayed(Message, long) post方式的方法可以将一个Runable对象排列到消息队列中。sendMessage方式的方法可以通过 Handler的handleMessage(Message) 方法携带有bundle类型的数据的Message对象到队列中(需要你实现Handler的子类)。【message】
定义一个message包含描述信息和任意的数据对象发送给Handler。这个对象包含两个额外的int类型的属性和一个Object类型的属性,它可以让你不需要去做一些强制类型的转换的操作。
1) arg1 和 arg2 都是Message自带的用来传递一些轻量级存储int类型的数据,比如进度条的数据等。通过这个数据是通过Bundle的方式来转载的,读者可以自己查阅源代码研究。
2) obj 是Message自带的Object类型对象,用来传递一些对象。兼容性最高避免对齐进行类型转换等。 3) replyTo 是作为线程通信的时候使用. 4) what 用户自定义的消息码让接受者识别消息种类,int类型。 获得Message的构造方法最好的方式是调用Message.obtain() 和 Handler.obtainMessage()方法。以便能够更好被回收池所回收,而不是直接用 new Message的方式来获得Message对象【避免使用不当造成内存泄露】
使用handler不当会造成内存泄露比如下面一段代码
当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC 检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有 Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler 的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。(参考自)
通常我们使用弱引用来处理,比如如下的方式就非常优雅
转自:http://blog.csdn.net/xsf50717/article/details/50557665