- 作用
- 运行流程
- 组成
- 同步屏障
- 内存泄漏
- Handler的生产者消费者模式
- 子线程创建的Looper消息队列无消息的时候的处理方案是什么
1.作用
Handler是用于线程间通信的,但是它产生的根本并不只是用于UI处理,而更多的是handler是整个app通信的框架
可以在ActivityThread里面感受到,整个App都是用它来进行线程间的协调
2.运行流程
handler.sendMessage() 【handler发送消息】 ->
messasgeQueue.enqueueMessage() 【消息队列队列的插入节点】 ->
looper.loop() 【从消息队列中取消息】 ->
messasgeQueue.next() 【从消息队列中取消息】 ->
handler.dispatchMessage() 【handler分发消息】 ->
handler.handleMessage() 【handler处理消息】 ->
引用关系
3.组成
(1)Looper
1.构造方法,每一个线程都有自己独有的Looper,独有的消息队列
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在Looper的构造方法中new了一个MessageQueue实现了唯一
并存入了创建Looper的线程
2.每一个Looper都需要执行prepare()方法
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
每一个线程都持有一个static final ThreadLocal sThreadLocal = new ThreadLocal()
在prepare()方法中实现了一个线程只有唯一的Looper ->
key为ThreadLocal,value为Looper
sThreadLocal.get() != null,说明当前线程已经有唯一的Looper
sThreadLocal.get() == null,就为当前线程创建一个Looper
3.主线程和子线程创建Looper
主线程在ActivityThread的main()函数中调用了Looper.prepareMainLooper()和Looper.loop()方法
而子线程如果需要创建自己的Looper需要手动调用Looper.prepare()和Loop.loop()方法
4.loop()方法
用一个for死循环不断从MessageQueue中取消息
for (;;) {
Message msg = queue.next();
...
}
在for循环里调用handler.dispatchMessage()方法分发消息
msg.target.dispatchMessage(msg);
(2)Handler
1.构造方法
让Handler持有Looper和Looper里的MessageQueue
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
2.sendMessageAtTime()发送消息
所有的发送消息最终都会调用sendMessageAtTime()方法
在sendMessageAtTime()中再调用Handler.enqueueMessage()方法
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
3.enqueueMessage插入消息队列
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在Handler.enqueueMessage()方法中
会让要发送的Message持有当前Handler的引用 -> msg.target = this
(3)MessageQueue
1.public boolean enqueueMessage()
简单的插入操作
优先级队列 -> 根据Message的when来判断插入的位置
2.Message next()
从MessageQueue队列中取Message
(4)Message
1.public static Message obtain()
可以直接new Message 但是有更好的方式 Message.obtain
因为可以检查是否有可以复用的Message,用过复用避免过多的创建、
销毁Message对象达到优化内存和性能的目地
也可以调用Handler.obtainMessage()方法
2.recycle()
当Message被处理掉时,应该调用recycle()方法回收Message,以便下次使用
4.同步屏障
1作用
同步屏障就是阻碍同步消息,只让异步消息通过
线程的消息都是放到同一个MessageQueue里面,取消息的时候是互斥取消息
而且只能从头部取消息,而添加消息是按照消息的执行的先后顺序进行的排序
解决,同一个时间范围内的消息,如果它是需要立刻执行的,开启绿色通道
2.开启同步屏障
MessageQueue.private int postSyncBarrier(long when)方法
postSyncBarrier()方法往MessageQueue中插入了一个target=null
即没有持有Handler的Message插入到合适的位置
3.从消息队列拿消息时Message,next()
拿到MessageQueue的头结点,如果他的target=null,说明它就是屏障
于是do-while循环拿到第一个异步消息进行处理
4.移除屏障 removeSyncBarrier(int token)
5.实例ViewRootImpl
在postCallback中用Handler发送了一个异步消息
5.内存泄漏
(1)原因
如果,循环体中有消息未处理(Message 排队中、Message延迟处理)
那么 Handler 会一直存在
根据可达性分析无法回收Message
自然Activity.onDestory()也无法执行
(2)解决方法
参考
static + 弱引用
在工作线程中执行
6.Handler的生产者消费者模式
MessageQueue是一个优先级队列
但是Handler中并没有对MessageQueue满的阻塞操作
所以可以无限制的往MessageQueue里放入Message -> 但是会导致OOM
是因为系统间也要通过主线程的Handler发消息
如果限制Message有满的话会导致系统消息不能存入,卡死
但是对于MessageQueue取消息时有阻塞状态
(1)取出来的Message没有到执行时间 -> 有限唤醒,自动唤醒
(2)取不出消息 -> 无限等待,等待唤醒
(1)取出来的Message没有到执行时间 -> 有限唤醒,自动唤醒
Looper.loop()取消息
调用MessageQueue.next()方法时
会计算需要等待的时间,到时间自动唤醒
(2)取不出消息 -> 无限等待,等待唤醒
会进入无限等待
直到handler.enqueueMessage()插入新消息时
调用MessageQueue.enqueueMessage()时唤醒
让MessaheQueue.next()方法继续执行
对于主线程来说:
如果消息队列没消息
那就直到handler.enqueueMessage()插入新消息时
调用MessageQueue.enqueueMessage()时唤醒
对于子线程来说
如果消息队列没消息
调用Looper.que()方法
再调用MessageQueue.quit()方法
唤醒MessageQueue.next()方法
并给mQuitting = true
在MessageQueue.next()方法中
返回一个空
接着在Looper.loop()中取到一个空Message
结束loop循环
7.子线程创建的Looper消息队列无消息的时候的处理方案是什么
Looper里有quit()和quitSafely()方法
最终调用到MessageQueue.quit()方法
quit()清空MessageQueue所有消息
quitSafely()清空所有延迟消息