简简单单的Handler源码分析
Handler机制如何保证线程之间的通信
Handler采用了内存共享的方案来实现线程间的进程通信, 为什么这么说呢? 接下来我会简单的分析一下.
- 在Handler机制中有下面几个类起到关键作用
Message
Message是Handler机制中最基础的类, 也是需要”new”最多的类, 由于在Java中大量创建对象的操作会引起内存的碎片化, 可能会大对象(例如Bitmap)的空间申请造成困难从而诱发JVM进行频繁的GC以致于影响app性能, 当然还存在内存抖动等问题.
对此, Google的Android工程师是通过一个对象池复用机制来减少以上问题. 所以当我们需要一个Message对象的时候就可以通过Message.obtain方法. 当我们”使用完”一个Message的时候, 也就是looper准备去获取下一个Message之前会将Message回收掉.1
msg.recycleUnchecked();
MessageQueue
MessageQueue这个类是实现整个Handler机制的关键, 也是内存共享的核心, 在生产消费者模型中属于仓库的位置. 而且, 在它的next方法和enqueueMessage方法中调用了native方法来让线程让出时间片和尝试唤醒线程保证了线程的正常运转, 而在native层c++代码会去调用linux系统的相关服务,这里就不再深入了.
- Handler
Handler是一个Message的起始也是终止的地方. 如果把Message的传递简单看成生产消费者模型, 那么它既是生产者也是消费者.
关于Handler对Message消息的处理进行了三层分级
- 检查 msg.callback
- 检查 mCallback
mCallback.handleMessage(msg); - handleMessage
这种类似责任链模式的处理方式极大的提高了程序的灵活性, 在View的事件分发过程中也有体现.
- Looper
Looper它是一个比较特殊的类, 这里说的是它的设计思想很巧妙, 也可以说它是ThreaLocal类在Android中的最佳实践.因为它的一系列设计保证了Looper—>MessageQue的线程隔离和线程内单例. 这里不得不说一下ThreadLocal这个类, 由于Thread类中内置了ThreadLocalMap对象, 让它天然就具有实现线程的隔离的可能.
它利用了ThreadLocalMap的Key(ThreadLocal变量本身, 这里采用了hash函数去效验)是唯一的这一特性, 在myLooper方法中使用了一种伪单例的方式(直接抛出错误)禁止开发者去重复创建它的实例.这样以来它在私有构造方法中实例化的MessageQueue也得到了唯一性的保证.
prepareLoop方法
loop方法
从sendMessage/postMessage出发
不管是从sendMessage从还是从post方法最终都会调用到Handler.enqueueMessage方法, 接着会调用Handler对应MessageQueue.enqueueMessage方法(Looper和MessageQueue都与Handler存在1对>=1的关系). 而且这个入队方法考虑了线程安全问题(毕竟Handler本身就是用与线程通信嘛)对大部分的代码都加了sycronized锁.
在队列的另外一头, looper所在的线程需要要去开启loop去轮询消息队列.所以首先这里先从Looper.loop方法入手, loop方法首先去做了一些验证
主线程的Looper开启过程
- 主线程(ActivityThread)创建时(main方法)就创建了looper并调用了looper的loop方法
- sendMessage和postMessage最终会调用SendMessage的方法
- sendMessage => MessageQueue.enqueueMessage(msg,uptimeMills)
主线程中开启的Looper为什么不会导致ANR
- 因为在不需要无限循环的时候(如队列中没有可以处理的消息(消息设定的处理时间还没到或者队列中没有消息)就会阻塞线程直到有新的可以处理的消息就会去唤醒主线程.
如何避免Looper的内存泄漏问题
在Looper的生命周期应该结束的时间点去调用它的quitSafely方法
Handler中使用到了哪些设计模式
享元模式 在looper中会调用recycleUnckecked方法进行回收利用
责任链模式 通过分发Message进行分层处理,极大的提高了程序的灵活性.