前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Android 异步操作】手写 Handler ( 消息队列 MessageQueue | 消息保存到链表 | 从链表中获取消息 )

【Android 异步操作】手写 Handler ( 消息队列 MessageQueue | 消息保存到链表 | 从链表中获取消息 )

作者头像
韩曙亮
发布2023-03-28 18:48:12
1.3K0
发布2023-03-28 18:48:12
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目录

一、MessageQueue 消息队列存储消息


Message 链表 : 消息队列 MessageQueue , 内部维护了一个 Message 链表 , 存储的时候只存储第一个 Message 即可 ;

链表插入元素 : 当 Handler 在其它线程调用 sendMessage 方法 , 将 消息 Message 放入 Looper 中的 MessageQueue 时 , 针对该链表的操作就是 , 循环获取链表的下一个元素 , 最终 获取到最后一个元素 , 最后一个元素的 next 为空 ; 将 最后一个元素的 next 设置为本次要插入的 Message , 即可完成消息存储到消息队列的操作 ;

链表元素同步 : 链表为空时 , 取出链表的操作会阻塞 , 调用的是 wait 方法 , 此时有消息加入链表后 , 需要 调用 notify 唤醒阻塞 ;

消息入队的部分代码 :

代码语言:javascript
复制
    /**
     * 该队列是一个链表 , 因此这里只给出第一个 Message 即可
     */
    Message mMessage;

    /**
     * 将 Message 消息加入到 Message 链表中
     * @param msg
     */
    public void enqueueMessage( Message msg ){
        // 因为 该消息队列 可能会有多个线程 通过 Handler 向消息队列中添加消息
        // 因此 需要使用同步代码块包裹以下逻辑
        synchronized (this){
            if( mMessage == null ){
                mMessage = msg;
            }else{
                /*
                    如果链表不为空
                    这里需要循环查找消息队列的最后一个消息
                    将本次传入的 Message msg 参数加入到链表尾部
                 */
                Message pointer = mMessage;
                Message previous = pointer;
                for(;;){
                    // 记录上一条消息, 每次遍历都将本次遍历的记录下来
                    previous = pointer;

                    // 将 pointer 指向下一条消息
                    pointer = pointer.next;

                    // 此时如果某个 Message 的 下一个元素为空
                    // 说明该 Message 是消息队列最后一个元素
                    if(pointer == null){
                        break;
                    }
                }
                // 将本次参数传入的 Message 放到链表最后
                previous.next = msg;
            }
            notify();
        }
    }

二、MessageQueue 消息队列取出消息


Looper 调用 loop 方法后 , 会一直循环 , 不断地从 消息队列 MessageQueue 中取出 Message 消息 , 然后 将 Message 消息发送给对应的 Handler 执行对应的操作 ;

从 消息队列 MessageQueue 中取出消息 , 也是 取出链表表头 的操作 , 取出该链表的表头 , 然后 将表头设置成链表的第二个元素 ;

消息同步 : 如果当前链表为空 , 此时会 调用 wait 方法阻塞 , 直到消息入队时 , 链表中有了元素 , 会调用 notify 解除该阻塞 ;

代码语言:javascript
复制
    /**
     * 从消息队列中获取消息
     * @return
     */
    public Message next(){
        synchronized (this){
            // 本次要获取的消息, 最后要返回到 Looper 中 loop 方法中
            Message result;

            for (;;){
                // 尝试和获取 消息队列 链表中的第一个元素
                result = mMessage;
                if(result == null){
                    // 如果当前的 Message 队列为空 , 阻塞等待 , 直到新的消息到来
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    // 如果不为空 , 说明已经获取到最终的消息 , 退出循环即可
                    break;
                }
            }

            // 处理链表逻辑 , 将表头指向下一个 Message
            mMessage = mMessage.next;

            return result;
        }
    }

三、消息队列完整代码


代码语言:javascript
复制
package kim.hsl.handler;

public class MessageQueue {

    /**
     * 该队列是一个链表 , 因此这里只给出第一个 Message 即可
     */
    Message mMessage;

    /**
     * 将 Message 消息加入到 Message 链表中
     * @param msg
     */
    public void enqueueMessage( Message msg ){
        // 因为 该消息队列 可能会有多个线程 通过 Handler 向消息队列中添加消息
        // 因此 需要使用同步代码块包裹以下逻辑
        synchronized (this){
            if( mMessage == null ){
                mMessage = msg;
            }else{
                /*
                    如果链表不为空
                    这里需要循环查找消息队列的最后一个消息
                    将本次传入的 Message msg 参数加入到链表尾部
                 */
                Message pointer = mMessage;
                Message previous = pointer;
                for(;;){
                    // 记录上一条消息, 每次遍历都将本次遍历的记录下来
                    previous = pointer;

                    // 将 pointer 指向下一条消息
                    pointer = pointer.next;

                    // 此时如果某个 Message 的 下一个元素为空
                    // 说明该 Message 是消息队列最后一个元素
                    if(pointer == null){
                        break;
                    }
                }
                // 将本次参数传入的 Message 放到链表最后
                previous.next = msg;
            }
            notify();
        }
    }

    /**
     * 从消息队列中获取消息
     * @return
     */
    public Message next(){
        synchronized (this){
            // 本次要获取的消息, 最后要返回到 Looper 中 loop 方法中
            Message result;

            for (;;){
                // 尝试和获取 消息队列 链表中的第一个元素
                result = mMessage;
                if(result == null){
                    // 如果当前的 Message 队列为空 , 阻塞等待 , 直到新的消息到来
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    // 如果不为空 , 说明已经获取到最终的消息 , 退出循环即可
                    break;
                }
            }

            // 处理链表逻辑 , 将表头指向下一个 Message
            mMessage = mMessage.next;

            return result;
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-11-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一、MessageQueue 消息队列存储消息
  • 二、MessageQueue 消息队列取出消息
  • 三、消息队列完整代码
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档