【数据结构】线性表 ③ ( 链表示例 | Android MessageQueue 源码分析 | enqueueMessage 添加消息 | next 取出消息 )

2023-10-11 16:50:34 浏览数 (2)

一、Android MessageQueue 源码分析

在 Android 系统中的 Handler 机制 中 , 涉及到了 Handler , Message , Looper , MessageQueue 等组件 , 其中 MessageQueue 是消息队列 , 其中包含了很多 单链表 元素 ;

MessageQueue 源码地址 : https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/android/os/MessageQueue.java

Message 源码地址 : https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/android/os/Message.java

1、Message 消息

在 Message 消息的源码中 , 包含了 Message next 指针 , 指向单链表中的下一个 Message 节点 ;

代码语言:javascript复制
    // sometimes we store linked lists of these things
    /*package*/ Message next;

参考源码 : android/os/Message.java#115

2、MessageQueue 消息队列 enqueueMessage 添加消息

在 MessageQueue 消息队列 中的 boolean enqueueMessage(Message msg, long when) 函数 是 向 消息队列 中添加 Message 消息的方法 ;

该函数的作用是 在 long when 时间后 , 执行 Message msg 消息 ;

添加 Message msg 消息时 , 需要参考 long when 时间参数 , 时间越长 Message 排序越靠后 , 时间越短 Message 的位置越靠前 ;

MessageQueue 消息队列 , 是 按照执行时间进行排序的 ;

在下面的代码逻辑中 , 一直进行死循环 , 判断当前的 Message 节点指针 p 指向的节点 , 如果该节点的时间小于传入的 long when 时间参数 , 那么找到了该节点 , 循环结束 , 插入节点 ;

插入节点的操作 , 只需要两行代码 , 改变指针的指向即可 , msg.next = p; prev.next = msg;

该 MessageQueue 几乎不需要查询遍历 , 但是会 频繁进行插入操作 , 因此在该场景下 , 使用单链表是最佳选择 ;

大部分情况下 , Handler 发送 Message 都会将 Message 消息插入到 Looper 中的 MessageQueue 的最前面 , 如果要使用顺序表 , 则每次插入 , 都需要挪动整个顺序表中的元素 ;

代码语言:javascript复制
                // 插入到队列的中间。通常我们不需要唤醒事件队列,
                // 除非在队列的头部有一个障碍,并且该消息是队列中最早的异步消息。
                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;
                    }
                }
                // 插入 Message 节点
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;

参考源码 : android/os/MessageQueue.java#564

3、MessageQueue 消息队列 next 取出消息

在 MessageQueue 消息队列 中的 Message next() 函数 , 就是从 MessageQueue 消息队列中取出消息 , 并处理消息的函数 ;

Message next 函数位置 : android/os/MessageQueue.java#326

直接调用 msg = msg.next; 获取下一条消息 , 获取消息后 , 判定该消息的执行时间是否到了 , now < msg.when , 如果时间到了 , 就执行该消息 , 如果时间没有到 , 则设置超时唤醒 ;

代码语言:javascript复制
                // 尝试检索下一条消息。如果找到,返回。
                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) {
                    if (now < msg.when) {
                        // 下一条消息尚未准备好。设置一个超时唤醒当它准备好了。
                        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 {
                    // 没有更多的信息。
                    nextPollTimeoutMillis = -1;
                }

0 人点赞