五个紧密相关的对象:
Handler
Message
MessageQueue
Looper
Thread
Handler
postMessage -> MessageQueue.enqueMessage()
sendMessage -> MessageQueue.enqueMessage()
image.png
最终都会调用enqueMessage()方法;
dispatchMessage():
image.png消息分发,最终会调用handleMessage()方法
handleMessage():
消息处理
MessageQueue:
优先级队列,实现方式-链表:根据时间进行排序的队列,队头的消息最早执行,队尾的消息最后执行
enqueMessage():
第一点:将msg.target赋值为发送消息的handler
第二点:存储消息,把消息放到messageQueue中来,与消息队列中的消息进行时间对比,如果待插入的消息的执行时间先于其中的某一消息,则插入到该消息之前,如果sendMessage的delay的时间为0,则是立即执行。
next()函数:
从messageQueue中取消息,并以Message对象形式返回给调用者,
如果消息队列中没有消息,nextPollTimeoutMillis会置为-1,当判断nextPollTimeoutMillis为-1时,会调用nativePollOnce函数等待
nativePollOnce调用->linux->epoll机制->进行无线等待
Looper:
Looper.loop():
死循环,线程在调用(或者Looper在调用)Looper.loop()函数,该函数中会调用MessageQueue中的.next()函数,并将取得的消息调用Handler的dispatchMessage方法,在dispatchMessage方法中最终会调用Handler的handleMessage方法
关联面试问题:
一个线程有几个Handler?
无限个,可以随意创建
一个线程有几个Looper?
只有一个
####怎么保证只有一个Looper?
ThreadLocal保证一个线程只有一个looper
ThreadLocal:
是一个<Key,Value>的map,在Looper.prepare()函数中会先进行判断是否存在looper的判断,如果不为空则抛出异常,如果为空则创建Looper并通过ThreadLocal.set()函数存放到ThreadLocal中。
ThreadLocalMap:
用于保存当前线程上下文,一个线程对应一个ThreadLocalMap
线程和Looper怎么进行绑定?
ThreadLocal.set():方法中首先获取了当前线程和当前线程对应的ThreadLocalMap两个对象,并将ThreadLocal对象作为key,Looper作为值存放到了ThreadLocalMap当中。ThreadLocalMap确定了一个ThreadLocal对应一个Looper,又根据上述一个线程对应一个ThreadLocalMap,确定了一个线程对应一个Looper
Handler内存泄漏的原因?为什么其他内部类没有这个问题?
匿名内部类持有外部类对象,Handler持有当前activity:
由enqueMessage()函数的概念中的第一点得知,当handler发送消息时会将当前handler赋值给msg.target。得出如下逻辑:MessageQueue持有->Message->包含handler->handler持有activity
为何在主线程中可以new Handler?在子线程中new Handler需要做什么准备?
一、在开启app时zygote会fork一个进程 -> zygote会为该进程创建一个虚拟机 -> 会调用ActivityThread的main()函数 -> main函数中会调用Looper.prepareMainLooper()函数,初始化Looper。所以在主线程中可以随意new Handler,因为looper已经准备好了。
二、在子线程中如果想要创建handler,需要先调用子线程的Looper.prepare()和looper.loop方法
子线程中维护的looper,消息队列无消息的时候的处理方案是什么?有什么用?
一、当MessageQueue.next()函数发现没有消息的时候,在next()函数中,会触发liunx层的epoll机制进入无限等待,直到再有新消息出现。在Looper.loop()函数中如果MessageQueue.next()返回的消息为null的时候会直接终止循环,跳出loop函数,直到下一次消息进入
二、looper.quite(),1,调用MessageQueue的quite()方法,2,MessageQueue.quite()方法remove所有消息,3,不管有没有消息,唤醒线程,调用loop方法,当loop方法发现没有消息时会停止loop循环
三、通过handler.getLooper()可以获取到子线程的looper,因此可以调用looper.quite方法。
主线程需要释放吗?为什么?
不需要,主线程调用quite函数时,会报异常。
既然可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能处于不同线程),那它内部是如何确保线程安全的?
一个线程->一个looper->一个messageQueue
方法加锁synchronized,锁MessageQueue对象,防止在执行一个操作时其他线程对队列进行操作
使用Message时该如何创建?
handler.obtainMessage(),obtain采用享元设计模式,减少了Message对象的创建和销毁过程,降低了过多message时造成的内存抖动
Looper死循环为什么不会导致应用卡死?
产生ANR的问题不是因为主线程睡眠了,而是因为输入事件没有响应,输入事件没有响应他就没有办法唤醒这个looper,所以才加了5秒限制。消息队列是顺序执行,当执行到一个msg过于耗时时,导致阻断了messageQueue不能继续执行,所以才导致了ANR。
looper中没有消息时loop函数会退出,唤醒线程的方法?
1,looper中添加message,通过nativeWaite()->loop运作
2,输入事件
网友评论