文章包括四部分:
(1)什么是handler?
(2)handler的使用方法。
(3)handler机制原理。
(4)handler引起的内存泄漏以及解决办法。
一.什么是handler
在viewRootImpl类中有一个checkThread()方法,在该类中做了判断,只有在创建view的原始线程才能更新ui,也就是只有在主线程中才能更新ui,在子线程中更新ui就会抛出该异常,根本原因是Android中的view不是线程安全的,在Android的应用启动时会自动创建一个线程,就是程序的主线程,主线程主要负责ui的展示,事件的分发等,我们会把主线程称作ui线程,在Android中ui控件是非线程安全的,我们引进Handler在子线程中发送消息给主线程更新ui,在子线程中做耗时操作,handler是Android中消息机制的上层接口。
总结:handler是通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue,让自己想要处理的耗时操作放在子线程,让更新ui的操作放在主线程。
二.handler的使用方法
(1)post(Runnable)
(2)sendMessage(message)
在底层两种使用方式最终调用的都是sendMessageAtTime()这个方法将消息放入消息队列中。
三.handler机制原理
Looper:是每一个线程所独有的,通过Looper.loop()读取MessageQueue当中的消息,读到消息后,把消息发送给Handler来处理。
MessageQueue:是消息队列,是先进先出的方式来管理Message的,采用单链表的数据结构存储消息,在创建Looper的时候已经创建了MessageQueue,Looper和MessageQueue已经关联到了一起。
Message:是消息对象,其中有很多参数what,arg1,arg2,obj。
Handler:有两个作用,一是发送消息,二是处理消息,处理的就是Looper发送过来的消息,而Looper发送消息也是通过handler来进行的。
首先可以看到,在handler的构造方法中,创建了Looper,并且通过Looper创建了MessageQueue
Looper是如何获取的呢?可以看到Looper是通过ThreadLocal的get方法获取的。
ThreadLocal的作用是通过不同的线程,访问同一个ThreadLocal,不管是set方法还是get方法,他们对ThreadLocal所做的读写操作仅限于各自线程的内部,这就是为什么handler里面要通过ThreadLocal来保存looper,这样就可以使每一个线程有单独唯一的looper,那ThreadLocal是什么时候调用的set方法呢?
可以看到在Looper类中有一个方法叫做prepareMainLooper(),在这个方法中调用了prepare(),在prepare()中,调用了ThreadLocal的set方法,设置了Looper。在程序一创建的时候就会创建一个主线程,ActivityThread在其中的main方法中调用了prepareMainLooper()
总结:在程序一创建的时候,创建了主线程,其中ActivityThread的main方法中调用了prepareMainLooper(),在prepareMainLooper()中调用了prepare,在prepare中设置了looper,在使用handler的时候,就会去获取get当前线程这个looper.这也是主线程可以默认使用handler的原因。
四.handler引起的内存泄漏以及解决办法
原因:创建的handler不是静态内部类,会持有外部类的引用,activity要回收的 时候无法回收,导致内存泄漏。
解决办法:
1.是将handler设置为静态内部类。
2.在onDestroy()时调用handler.removeCallback()将handler移除。
3.handler持有外部类的弱引用。
注意:
1.每个线程只能拥有一个looper。
2.一个looper可以绑定多个线程的handler。
3.多个线程可往一个looper所持有的MessageQueue中发送消息,提供了线程间通信的可能。