Handler 机制
handler 的出现是为了解决子线程无法更新主线程的问题。
Handler 的使用
- 构造函数
|
|
第1个和第二个构造函数的源码如下:
|
|
第3个和第四个同时传递了Looper对象,源码如下:
|
|
在 第二个和第四个中还有Callback 参数,是handler的内部接口,代码如下:
|
|
handleMessage 的返回值是一个boolean值,如果为true,表示拦截msg的信息;如果为false,表示不拦截;
|
|
创建
- 在主线程中
|
|
- 在子线程中
|
|
同样的我们也可以利用 HandlerThread来创建handler;如下:
|
|
Post /Send
SendMessage
根据使用场景不同,封装了不同是方法:
|
|
通过阅读源码,我们发现上述所有的方法最终都是通过调用sendMessageAtTime()方法来达到发送消息的目的;源码如下:
|
|
Post Runnable
封装的方法如下:
|
|
通过源码的阅读,我们发现post相关的方法源码如下:
|
|
很明显,post相关的方法底层也是调用send的方法去实现的;将Runnable转化为Message的方法是 getPostMessage(r),源码如下:
|
|
两种方法的区别
- 当发送者知道如何处理消息时,使用
Handler.post()
方法,发送者直接在run()
中进行处理; - 当接受者知道如何处理消息的话,使用
Handler.sendMessage()
方法,发送者将Message 对象发送出去,handler调用 handleMessage() 方法进行处理;
Handler 的好帮手
Message 、Looper 、MessageQueue
Message
Creatror
|
|
Message 实现了Parceable接口,Message包含:what、arg1、arg2、obj四个属性,同时可以传递Bundle,调用 setData() 方法;通过调用 setTarget() 可以指定接收的handler;
Looper
俗称消息泵,它的职责是源源不断的从消息队列中取出消息交给handler 去处理,如果队列中没有消息,那么它就会阻塞等待;
Looper 的 prepare()
方法代码如下:
|
|
这个方法中最关键的就是 sThreadLocal 对象的 get() 和 set() 方法,这个方法涉及到一个java关键字ThreadLocal
;它的作用是:保证线程中变量的唯一性;相应的 get 和 set 方法:
|
|
ThreadLocalMap 是一个静态内部类,其实自定义的 hashMap;将当前 ThreadLocal 对象作为 key,传入的泛型参数值作为 value,这样就达到了“在线程作用域下的变量存储”的效果。
核心方法loop()
,源码如下:
|
|
关于Looper的代码,我们就先分析到这里;
大家可能又一个问题,那就是:loop 中的死循环难道不会阻塞主线程么?,在知乎上找到这个问题的答案:https://www.zhihu.com/question/34652589
MessageQueue
MessageQueue 是一个有单链表实现的先入先出的队列;
在Looper.loop() 中出现的next() 方法,其实就是不断的从 MessageQueue 中取出Message对象;源码如下:
|
|
在看 enqueueMessage() 方法,源码如下:
|
|
总结
在handler中调用 post () 和 send() 方法,两者最终都会调用sendMessageAtTime()方法,完成将 Message 加入到 MessageQueue 中;
|
|
handler 中的 enqueueMessage() 方法则调用了 MessageQueue 中的 enqueueMessage() 方法,从而完成消息入队列的操作;
|
|
在Looper.loop() 方法中不断调用 MessageQueue.next() 方法取出 Message 对象,并将其返回 Handler 处理,或者调用 Message.callback() 的run方法来处理;
Handler 使用中需要注意的问题
内存泄漏
针对 Handler 引起的内存,我们可以通过以下几种方式去避免,
- 静态内部类、弱引用
|
|
- 全局ApplicationContext,取代原先 ActivityContext.
- 在生命周期的最后调用
handler.removeCallbacksAndMessages(null)
方法
我在面试中,被人问到过说一下 handler 机制,这个一个很平常的问题,但是也是最不好回答的问题,我们不能简简单单的说一下那些已经被说烂的东西,同时我们没有底层的源码做支撑,我们也不好说底层,那么我们的办法就是把框架层的东西说出来,这至少是我们看过的东西。
有一个同事曾经问过我,Handler 为啥会跟线程有关系?当时我没想明白,后来通过阅读源码,我知道了,简单总结一下:handler 创建的时候跟 Looper 产生了关系有一个默认的Looper,同时 Looper 的源码中有一个 ThreadLocal 变量,这个关键字保证了线程中变量的唯一性,很显然,handler 是通过 Looper 这个中间量和 Thread 产生了联系。个人的拙见不对的地方欢迎大家指正。
到此为止,Handler 的源码和运行机制我们就分析完啦。