handler通信实现的方案实际上是一种内存共享的方案
为什么线程间通信不会干扰,加了锁,内存管理设计的很完善
通过loop.loop启动loop.就开始了一个死循环,循环去队列中进行取值
loop取到一个为null的message,则会退出handler发送消息,不管send post到最后,都会到sentMessageAtTime,在这个方法里面,调用到enqeueMessage.在这个方法内,接着走到了queue.enqeueMessage()这里会把时间穿进去
这个方法内,会把消息插入队列中(排序算法:插入排序)。messageQueue next方法,会返回message消息,相当于取消息的方法
这个next的方法调用,会在looper里。在loop方法内,有个for死循环,在这里会调用queue.next方法。然后取出message后,在这里方法里,接着调用到了msg.target.diaptachMessage方法。
在这个方法内,会走到handleMessage消息队列,messageQueue, 是一个优先级队列,由单链表实现的。 因为next取值的时候,默认取第一个,所以,他相当于先进先出的数据结构,也就是队列。
Loop
- 核心有三个 构造函数 loop ThreadLocal
- Loop类里有个私有的构造函数,所以其初始化有prepare方法完成
image.png -
所以说到loop的初始化,就必须说到ThreadLocal---他是一个线程上下文的存储变量.ThreadLocal本身是不存储数据的,他存储数据主要是因为内部的一个类ThreadLocalMap。
image.png
他存储数据是通过线程获取ThreadLocalMap。每一个线程都有一个ThreadLocalMap成员变量threadLocals
一个线程里只有一个loop,就是根据threadLocal保证的
ThreadLocal
- ThreadLocal 里有一个静态内部类ThreadLocalMap。实际存储数据的东西是ThreadLocalMap,他里面维护一数组,并对ThreadLocal求模获得下标进行存储。
- 而每个looper里有一个MessageQueue,所以,MessageQueue是跟looper绑定 ,所以也就是每个线程只有一个MessageQueue,并且其声明是final
而且 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();因为是static final,所以这个变量在整个Android唯一
handler内存泄漏的原因
是因为生命周期的问题,所以并不仅仅是内部类持有外部类引用,而是因为他们声明周期不同。之所以生命周期不同,是因为在handler sendmessage的时候,执行到handler的enqueueMessage的msg.target赋值为了当前handler,这个队列的生命存在周期可能很长,所以导致handler释放不了,然后handler拿着外部的引用,导致了外部也无法释放
子线程消息队列无消息后应该怎么做
- 调用looper.quit方法,此时会处理掉什么消息,并唤醒消息队列的循环(无消息的时候会让他睡眠),然后因为给一个判断的变量mQuitting赋值为true了,所以在循环操作里,监测到这个true
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
在此处messageQueue的next方法返回为null,
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
也就是Looper的loop方法里msg为null,然后就return了。
- 所以quit方法做了什么事?---唤醒睡眠的线程(因消息为null睡眠了)--MessageQueue里mQuitAllowed设置为true ---导致next返回null--导致loop方法里得到null后return 退出
为什么多个线程都可以往消息队列中放消息,是怎么保证线程安全的
- 因为加锁了
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
.....
}
.....
}
注意此处锁的是this,所以也就是对同一个这个对象的访问是有锁的,但是对不同对象是没事的,因为this不同
为什么取消息的时候也有synchronized (this)------因为从这个队列中取的时候,有可能有其他消息要进来,而且可能会插队(比如同步屏障或者延时消息的插队)
message的创建问题---大量new出来对象后为何不会内存抖动(full gc虽然会处理这些碎片,但是无法控制)和oom,其中涉及到了享元设计模式(内存复用,不是指的数据复用),在loop取出消息后,并没有直接将这个对象释放,而是msg.recycleUnchecked();