消息机制
Android中的消息机制你可以简单地理解成于生产者------消费者模型。非UI线程是生产者,他们通过Handler添加Message到MessageQueue中,并唤醒消费者;主线程消费者被唤醒后,就通过Looper取出MessageQueue中Message来执行。完整流程如下所示:
这里有几点需要注意:
- 消息机制是如何让Message在主线程被消费的?
我们都知道在java中创建的对象内存是分配在堆中,而堆是线程共有的。因此。当我们在其他线程给MessageQueue添加Message时,主线程也可以获取添加的Message。当然,这里有一个可见性问题,不过Android中的处理也很简单,就是在 MessageQueue 的添加方法中使用了 synchronized 代码块,来确保可见性。
java
//MessageQueue
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
}
return true;
}
- 为什么我们给Handler设置的Callback和重写的handlerMessage方法也会被执行
这是因为Handler发送Message时,Message会带上Handler的引用。在Looper中处理Message时,最后会调到Handler的dispatchMessage。它先会判断msg是否有callback,如果有就执行msg的callback。如果没有就执行Handler的callback。如果handler的callback返回false,或者没有handler的callback,就执行handleMessage方法。
同步屏障
同步屏障是一种让异步Message提前处理的机制,目的是让特定的Message提前执行,比如ViewRootImpl中的scheduleTravelas 方法就会发送一个同步屏障。
当MessageQueue发现有同步屏障的消息时,其判断依据是msg.target 是否为null,就开始遍历执行异步Message,这样异步消息就先执行了。需要注意的是同步屏障不会主动移除,它会一直留在MessageQueue中,只能主动删除。
java
//MessageQueue
public int postSyncBarrier() {
//默认when赋值的是开机时间运行时间
return postSyncBarrier(SystemClock.uptimeMillis());
}
//Handler
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//这里when赋值的是0
return enqueueMessage(queue, msg, 0);
}
不过实际上看源代码,同步屏障有一个问题,就是会被其他Message阻塞,它并不是优先级最高的。因此同步屏障消息不一定能及时执行到。这是因为同步屏障Message的when赋值的是开机时间运行时间,因此不是立即执行的。而Handler提供了sendMessageAtFrontOfQueue方法,它会把Message放在messageQueue链表头,实现方法就是设置Message的when为0。
IdleHandler
kotlin
handler.looper.queue.addIdleHandler {
//在空闲时执行逻辑
//返回值为false表明这个IdleHandler只需要执行一次,同时会被移除;为true时,则不移除
return@addIdleHandler false
}
IdleHandler是Android中提供的,在主线程空闲时执行逻辑的机制。
实现原理是:当有Message加入MessageQueue时,会唤醒在调用MessageQueue的next 方法时的阻塞。这时,他会查询Message 列表,看有没有需要现在执行的Message。如果没有,就会遍历 IdleHandler 的列表,并执行对应的方法。通过这种方式让IdleHandler在空闲时候执行。需要注意的是,在IdleHandler中最好不要执行耗时操作,不然会阻塞MessageQueue
内存泄漏
常见的就是Handler 泄漏;泄漏原因就是内部类引用了外部类,一般为activity。由于message 持有了 Handler,当message 延时很长时间比如说3分钟,这时就会发生内存泄漏问题。