1.简要概述
Handler负责发送Message到MessageQueue,Looper负责从MessageQueue取消息,最后消费者在Handler.handleMessage(Message msg)函数消费。
一个线程只能有一个Looper和一个MessageQueue(利用了ThreadLocal),一个Looper可以绑定多个Handler,Handler可以发送多个Message。
Handler发消息会把this指针写入message.target,最终messageQueue间接持有handler,此处解释Handler内部类泄漏问题(当handler持有context)。
2.屏障消息
Handler提供了静态方法构造一个异步消息 public static Handler createAsync(@NonNull Looper looper),看MessageQueue中next()中如何处理
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
//屏障消息开启后,此处只会取异步消息
}
可是屏障还没有开启,以上代码并不会执行,如何开启屏障:postSyncBarrier(),结束屏障:removeSyncBarrier(int token),需要配对使用否则同步消息无法执行,会一直在MessageQueue中。屏障消息妙用可以让该Handler发送的消息优先级最高,适合处理一些特殊的消息。
3.Looper一直在执行for死循环,为何不会造成线程卡死?从原理处进行分析,先上Looper.looper简化源码,其中调用了MessageQueue.next()简化代码
public static void loop() {
final Looper me = myLooper();
//此处解释在子线程创建的Looper一定要调用Looper.prepare,否则直接报异常
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
//从MessageQueue中取消息
Message msg = queue.next(); // might block
//没有消息则退出死循环
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
}
}
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;//返回nullLooper退出循环
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
//此处会调用native方法nativePollOnce是否让该线程阻塞
}
}
从上面分析,可以得出结论:Looper退出循环或阻塞后,为何后面发消息后依然可以处理,那还要看MessageQueue.enqueueMessage(Message msg, long when)中调用nativeWake(mPtr),此方法此处会唤醒线程同时调用静态方法Looper.loop()。通俗讲当该线程阻塞后会释放cpu资源,不会造成卡死,当有消息后线程被唤醒,继续执行消息(此处对native方法不做详细分析)。