说到Handler,相信学过Android的同学都能随口说出:这家伙是线程间通信的一种机制。但是,平时用来发送消息的方式有哪些?Handler是什么时候和Looper关联的?又是怎么关联的?消息在子线程中发出,在UI线程为什么能获取的到?这这这。。。太难了
Handler不能单独使用,内部配合着Message,Looper,MessageQueue以及ThreadLocal,这几个平时比较少直接操作,但却是Handler机制中必不可少的一部分。
本文采用Android SDK 29版本
首先从知道它们分别是什么开始吧
Handler
:发送和处理消息。Message
:他就是我们常说的消息的封装体。MessageQueue
:负责承载Message
消息,消息来时插入,Looper
来要时取出。Looper
:负责从MessageQueue
中取出消息,并交给Handler
处理。ThreadLocal
:通过搜狗百科查询到,它是JDK1.2
为解决多线程并发提供的一种方案,内部维护着一个Map,并不是Thead。这里的主要作用是在指定的线程储存数据(描述的可能不正确)。
平时的常规操作,分为Handler#sendMessage
和Handler#post
Handler消息发送
Handler#sendXxx
系列方法
Handler#postXxx
系列方法
无论是send
系列还是post
系列方法,最终都走到了Handler#sendMessageAtTime
,而Message
进入MessageQueue
则是在MessageQueue#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");
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
如果当前队列为空,则将消息作为队列头插入;否则在中间插入,并且插入依据是根据when(创建时间)
boolean enqueueMessage(Message msg, long when) {
//忽略一些判断代码
synchronized (this) {
//如果线程已经停止,则释放Message资源
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
//如果消息队列为空,则将当前消息放在队列头,并且如果当前队列处于阻塞的话,则唤醒它
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
//将消息从队列中间插入,
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
Handler和Looper
从上面写的代码来看,我们知道在调用post或send的时候,最终会把Message加入到队列中,但是没有看到Looper的身影啊,其实它是在Handler的构造方法中和Handler关联起来的。
public Handler(@Nullable Callback callback, boolean async) {
//忽略若干代码
//在这里获取当前线程的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//当前线程持有一个MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Looper#myLooper
中可以看到,从ThreadLocal中获取到当前线程的Looper。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
但是有get,那么就必然在哪里有set,通过搜索ThreadLocal#set
方法,可以看到在ThreadLocal#prepare
中将Looper set
到了ThreadLocal中
public static void prepare() {
prepare(true);
}
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));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
但是ThreadLocal#prepare
方法我找遍了Handler、Looper、Message、MessageQueue和ThreadLocal都找不到,原来它是在应用启动的时候,ActivityThread#main
中调用了
public static void main(String[] args) {
//忽略若干
Looper.prepareMainLooper();
//忽略若干
Looper.loop();
//忽略若干
}
到此,可以得出结论,Handler与Looper是在实例化一个Handler的时候关联起来的,并且从ThreadLocal中获取当前线程的Looper,并且在Looper#loop
中不断轮训,从MessageQueue取出Message
Looper与Message、MessageQueue
从前面的分析中我们知道,Handler通过post或send方法,最终调用了MessageQueue#enqueueMessage
方法处理消息的存入和取出。而Looper#loop
则不断轮训队列,将队列中的消息发射到Handler中。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// 忽略若干细节
for (;;) {
//从MessageQueue中取出下一条Message
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//...
try {
//target指的是Handler,通过dispatchMessage将消息分发
msg.target.dispatchMessage(msg);
//...
} catch (Exception exception) {
//...
} finally {
//...
}
//...
// 释放Message
msg.recycleUnchecked();
}
}
可以看到,在循环中不断通过MessageQueue#next
取出消息,然后通过Handler#dispatchMessage
分发出去,分别看看这两个方法。
Message next() {
//忽略...
for (;;) {
//忽略...
//Linux底层实现阻塞/唤醒的select、poll和epoll机制
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
Message prevMsg = null;
Message msg = mMessages;
//忽略...
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
//忽略...
}
}
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
到了这里,是不是看到了我们熟悉的handleMessage;一般情况下,sendXxx方法,都会执行到handleMessage,然后交由我们去处理,而postXxx一般会带Runnable进来到,所以会执行到run方法。这里分析基本就结束了。
当然其中UI线程中执行Looper#loop
为什么不卡死涉及到了Linux底层相关机制,本人水平有限就不深入研究了。
最后再总结一下:
- 初始化Handler的时候,同时会创建一个Looper,并与当前线程绑定。
- Handler发消息到MessageQueue中,在enqueueMessage中将消息插入队列。
- Looper的loop()从队列中不断循环取出,并且交给target.dispatchMessage()。
- 在
Handler#dispatchMessage
中判断是否存在Runnable,存在则回调到Runnable#run
方法,否则直接回调Handler#handlerMessage
- 需要注意的是,一个线程只存在一个Looper,一个Looper只去轮询一个队列,所以一个线程只存在一个队列
Handler其实在Android体系里面还是非常重要的,基本贯穿了整个框架,很有必要深入学习。
欢迎关注我的个人微信公众号,【优了个秀】和你每天进步一点点