收录:
Android Handler机制 - MessageQueue如何处理消息
Handler 如何做到阻塞
Android篇:2019初中级Android开发社招面试解答(中)
Handler消息机制组成:
- Message(消息):需要被传递的消息,消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息。
- MessageQueue(消息队列):负责消息的存储与管理,负责管理由 Handler发送过来的Message。读取后会自动删除消息,单链表维护,插入和删除上有优势。在其next()方法中会无限循环,不断判断是否有消息,有就返回这条消息并移除。
- Handler(消息处理器):负责Message的发送及处理。主要向消息池发送各种消息事件(Handler.sendMessage())和处理相应消息事件(Handler.handleMessage()),按照先进先出执行,内部使用的是单链表的结构。
- Looper(消息池/循环机制):负责关联线程以及消息的分发,在该线程下从 MessageQueue获取 Message,分发给Handler,Looper创建的时候会创建一个 MessageQueue,调用loop()方法的时候消息循环开始,其中会不断调用messageQueue的next()方法,当有消息就处理,否则阻塞在messageQueue的next()方法中。当Looper的quit()被调用的时候会调用messageQueue的quit(),此时next()会返回null,然后loop()方法也就跟着退出。
如何保证looper的唯一性
每个线程只有一个looper,而每个Thread中都又一个关键Threadlocal。是用于存放每个线程的looper对象的,存取的方式是通过get/set。相当于一个map的存放方式。键位key是当前线程的实例。value就是looper对象。所以每次创建looper都会去ThreadLocal里面找有没有当前线程的looper。
如何知道 message 发送到哪个handler处理
当使用 Handler.sendMessage() 发送消息时,调用 enqueueMessage 方法内有 msg.target = this 将 Handler 实例赋值给 msg 对象。当 loop() 取出消息时,调用 dispatchMessage 方法根据 target 属性,回调对应 handler 实例的 handlerMessage 方法处理消息。
具体流程图:
- App启动时创建一个主线程(UI),接着UI线程会创建一个Looper,同时也会在在Looper内部创建一个消息队列。而在创键Handler的时候取出当前线程的Looper,并通过该Looper对象获得消息队列(MessageQueue),然后Handler在子线程中通过MessageQueue.enqueueMessage在消息队列中添加一条Message。
- 通过Looper.loop() 开启消息循环不断轮询调用 MessageQueue.next(),取得对应的Message并且通过Handler.dispatchMessage传递给Handler,最终调用Handler.handlerMessage处理消息。
源码分析:
- 插入消息
MessageQueue.enqueueMessage:Handler调用sendMessage()发送消息,而Handler内部通过Looper对象得到MessageQueue对象后又调用MessageQueue.enqueueMessage方法。
boolean enqueueMessage(Message msg, long when) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//插入前先消息队列是否有消息,新的头,如果被阻止,则唤醒事件队列。
if (p == null || when == 0 || when < p.when) {
//将消息放进队列头部
msg.next = p;
mMessages = msg;
needWake = mBlocked;//指示next()是否被阻止在pollOnce()中以非零超时等待。如果阻塞,则需要唤醒looper
} 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.
/*插入队列中间。 通常,除非队列的开头有障碍并且消息是队列中最早的
异步消息,否则我们不必唤醒事件队列。(队列中消息不为空,并且next()也没有阻塞)*/
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;
}
// 如果looper阻塞/休眠中,则唤醒looper循环机制处理消息
if (needWake) {
nativeWake(mPtr);//唤醒
}
}
return true;
}
调用nativeWake唤醒(这部分内容出自头部连接,详细源码分析可看前辈的)
//android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr){
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake(){
mLooper->wake();
}
void Looper::wake(){
...
//往mWakeEventFd 中write 1,用以唤醒 looper
ssize_t mWrite = TEMP_FAILURE_READY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
}
既然有写入消息,那必定要把消息处理掉,所以唤醒了epoll_wait(),然后继续方法调动awoken(),这个方法就是将之前写入的1读出,表示消费这个事件
void Looper::awaken(){
...
//读取头部消息,静默处理掉
TEMP_FAILURE_READY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}
随后在Java 层的next()@MessageQueue 就被唤醒,读取在enqueueMessage()@MessageQueue 插在队头的消息进行处理
- Looper循环读取消息
当looper循环机制在MessageQueue的next()读取消息时发现消息队列中没有消息时,就会调用nativePollOnce(ptr, nextPollTimeoutMillis);将next()阻塞在PollOnce中。looper也就进入了休眠期。
@UnsupportedAppUsage
Message next() {
// 如果消息循环已经退出并被处理,请返回此处。
// 如果应用程序尝试退出后不支持的循环程序,则会发生这种情况。
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;//判断消息队列中是否有消息
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//就是在这里根据nextPollTimeoutMillis判断是否要阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 尝试检索下一条消息。 如果找到则返回。
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 被障碍挡住了。 在队列中查找下一条异步消息。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {//队列中拿到的消息不为null
if (now < msg.when) {
// 下一条消息尚未准备好。 设置超时以使其在准备就绪时醒来。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 正常返回处理
...
} else {
// 队列中没有消息,标记阻塞looper循环进入休眠
nextPollTimeoutMillis = -1;
}
// 现在已处理所有挂起的消息,处理退出消息。
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// 空闲句柄仅在队列为空或将来要处理队列中的第一条消息(可能是屏障)时才运行。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
...
}
...
// 将空闲处理程序计数重置为0,这样我们就不会再次运行它们。
pendingIdleHandlerCount = 0;
// 在调用空闲处理程序时,可能已经传递了一条新消息,
//因此返回并再次查找未处理消息,而无需等待。
nextPollTimeoutMillis = 0;
}
}