首页 > 其他分享 >Handler在有屏障消息和没有屏障消息时的区别

Handler在有屏障消息和没有屏障消息时的区别

时间:2024-12-04 10:57:05浏览次数:6  
标签:异步 屏障 next Handler 消息 msg null

在Android的消息处理机制中,MessageQueue 扮演着关键角色,负责管理和调度消息的执行顺序。Looper 通过不断调用 MessageQueuenext() 方法,从队列中取出消息并分发给相应的 Handler 进行处理。理解 next() 方法在有屏障消息和没有屏障消息时的不同处理流程,对于优化应用性能、避免界面卡顿至关重要。

本文将详细解析 MessageQueue.next() 方法在 有屏障消息没有屏障消息 两种情况下的执行流程,帮助开发者深入理解消息调度机制。

1.消息类型简介

MessageQueue 中,消息主要分为以下几种类型:

  • 普通消息(Synchronous Messages):默认通过 Handler 发送的消息,按照发送顺序和时间戳(when)插入到消息队列中。
  • 异步消息(Asynchronous Messages):通过设置 Handler 为异步模式或手动标记消息为异步,具有较高的优先级,旨在绕过某些同步机制,快速处理重要任务。
  • 屏障消息(Barrier Messages):特殊类型的消息,用于在消息队列中设置屏障,阻止普通消息的处理,仅允许异步消息通过。通常由系统内部使用,开发者不直接操作。

2.MessageQueue.next() 方法概述

MessageQueuenext() 方法负责从队列中取出下一个要处理的消息,并返回给 Looper 进行处理。其基本逻辑包括:

  1. 阻塞等待:如果队列中没有可处理的消息,next() 方法会阻塞,直到有新消息到来或超时。
  2. 消息遍历:遍历消息队列,查找符合条件的消息进行处理。
  3. 消息优先级:根据消息的类型(普通消息、异步消息、屏障消息)和时间戳决定处理顺序。

下面将详细介绍 next() 方法在 没有屏障消息有屏障消息 两种情况下的执行流程。

3.没有屏障消息时的 next() 执行流程

当消息队列中 不存在屏障消息 时,next() 方法的执行流程相对简单,主要依据消息的时间戳和类型来决定处理顺序。

执行步骤

  1. 阻塞等待next() 方法首先通过 nativePollOnce() 阻塞等待,直到有新消息到来或超时。

    nativePollOnce(ptr, nextPollTimeoutMillis);
    

  2. 同步块访问:进入同步块 synchronized (this),确保线程安全地访问消息队列。

  3. 获取当前时间

    final long now = SystemClock.uptimeMillis();
    

  4. 遍历消息队列

    • 遍历所有消息,查找 when 时间戳小于等于当前时间的消息。
    • 异步消息优先:如果存在异步消息(msg.isAsynchronous() 返回 true),则优先处理异步消息。
    • 普通消息按顺序处理:如果没有异步消息,按照队列顺序处理普通消息。
  5. 处理消息

    • 异步消息:移除并返回异步消息进行处理。
    • 普通消息:移除并返回普通消息进行处理。
  6. 等待机制:如果没有符合条件的消息,更新 nextPollTimeoutMillis,继续等待。

    简化代码示例
// MessageQueue.java
Message next() {
    while (true) {
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;

            Message nextMsg = null;
            long nextTime = Long.MAX_VALUE;
            Message asyncMsg = null;

            while (msg != null) {
                if (msg.when <= now) {
                    if (msg.isAsynchronous()) {
                        asyncMsg = msg;
                        break; // 优先处理异步消息
                    } else if (nextMsg == null) {
                        nextMsg = msg; // 记录第一个可处理的普通消息
                    }
                } else {
                    if (msg.when < nextTime) {
                        nextTime = msg.when; // 记录下一个消息的处理时间
                    }
                }
                msg = msg.next;
            }

            if (asyncMsg != null) {
                removeMessage(asyncMsg);
                return asyncMsg;
            }

            if (nextMsg != null) {
                removeMessage(nextMsg);
                return nextMsg;
            }

            long waitTime = nextTime - now;
            if (waitTime > 0) {
                nextPollTimeoutMillis = (int) Math.min(waitTime, Integer.MAX_VALUE);
            } else {
                nextPollTimeoutMillis = -1;
            }
        }
    }
}

4.有屏障消息时的 next() 执行流程

当消息队列中 存在屏障消息 时,next() 方法需要额外处理,以确保屏障消息的存在阻止普通消息的处理,仅允许异步消息通过。

执行步骤

  1. 阻塞等待:与无屏障消息时相同,通过 nativePollOnce() 阻塞等待。

    nativePollOnce(ptr, nextPollTimeoutMillis);
  2. 同步块访问:进入同步块 synchronized (this)

  3. 获取当前时间

    final long now = SystemClock.uptimeMillis();
  4. 检查是否存在屏障消息

    Message msg = mMessages;
    if (msg != null && msg.target == null) {
        // 存在屏障消息
    }
    
  5. 跳过屏障消息,查找异步消息

    Message prevMsg = null;
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());
    
    • 遍历:从屏障消息后开始,遍历消息队列,查找第一个异步消息。
    • 目标:确保只有异步消息能够被处理,普通消息被屏障阻止。
  6. 处理消息

    • 异步消息:如果找到异步消息且 when 时间戳已到,移除并返回该消息进行处理。
    • 无异步消息:继续等待,普通消息被屏障阻止。
  7. 等待机制:如果没有找到可处理的异步消息,更新 nextPollTimeoutMillis,继续等待。

简化代码示例

// MessageQueue.java
Message next() {
    while (true) {
        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) {
                if (now < msg.when) {
                    // 消息未到处理时间,继续等待
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 移除消息并返回
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 队列中无可处理消息,继续等待
                nextPollTimeoutMillis = -1;
            }
        }
    }
}

5.关键代码解析

以下是 MessageQueue.next() 方法在有屏障消息和没有屏障消息时的关键代码片段解析。

检查屏障消息

Message msg = mMessages;
if (msg != null && msg.target == null) {
    // 遇到屏障消息,跳过屏障,查找异步消息
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());
}
  • 条件判断msg.target == null 用于判断是否为屏障消息。
  • 遍历查找:从屏障消息后开始,查找第一个异步消息。

处理消息

if (msg != null) {
    if (now < msg.when) {
        // 消息未到处理时间,继续等待
        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
    } else {
        // 移除消息并返回
        mBlocked = false;
        if (prevMsg != null) {
            prevMsg.next = msg.next;
        } else {
            mMessages = msg.next;
        }
        msg.next = null;
        msg.markInUse();
        return msg;
    }
} else {
    // 队列中无可处理消息,继续等待
    nextPollTimeoutMillis = -1;
}
  • 时间判断:如果消息的 when 时间戳未到,则更新等待时间。
  • 消息移除:将处理的消息从队列中移除,并返回该消息进行处理。

6.示例流程

通过一个具体的示例,说明在有屏障消息和没有屏障消息时,next() 方法如何处理消息。

场景描述

假设消息队列中存在以下消息顺序:

  1. 普通消息Awhen = 1000ms
  2. 屏障消息Btarget = nullwhen = 1500ms
  3. 异步消息Cwhen = 1000ms
  4. 普通消息Dwhen = 2000ms
  5. 异步消息Ewhen = 1500ms

执行流程

没有屏障消息时
  1. 调用 next() 方法

    • nativePollOnce() 阻塞等待,直到有消息到来或超时。
  2. 同步块内部

    • 获取当前时间 now = 1000ms
    • 获取队列中的第一个消息 msg = 普通消息A
  3. 检查是否为屏障消息

    • msg.target != null,即普通消息A,不是屏障消息。
  4. 处理普通消息A

    • now >= msg.when,即 1000ms >= 1000ms
    • 移除并返回消息A进行处理。
  5. 下次调用 next() 方法

    • 获取当前时间 now = 1000ms
    • 获取队列中的第一个消息 msg = 异步消息C
  6. 处理异步消息C

    • now >= msg.when,即 1000ms >= 1000ms
    • 移除并返回消息C进行处理。
有屏障消息时
  1. 调用 next() 方法

    • nativePollOnce() 阻塞等待。
  2. 同步块内部

    • 获取当前时间 now = 1500ms
    • 获取队列中的第一个消息 msg = 屏障消息B
  3. 检查是否为屏障消息

    • msg.target == null,即屏障消息B。
  4. 跳过屏障消息,查找异步消息

    • 遍历消息队列,找到异步消息E。
  5. 处理异步消息E

    • now >= msg.when,即 1500ms >= 1500ms
    • 移除并返回消息E进行处理。
  6. 下次调用 next() 方法

    • 获取当前时间 now = 2000ms
    • 获取队列中的第一个消息 msg = 普通消息D
  7. 检查是否为屏障消息

    • msg.target != null,即普通消息D,不是屏障消息。
  8. 处理普通消息D

    • now >= msg.when,即 2000ms >= 2000ms
    • 移除并返回消息D进行处理。

处理顺序

  • 没有屏障消息时

    1. 异步消息C
    2. 普通消息A
  • 有屏障消息时

    1. 异步消息E
    2. 普通消息D

屏障消息B 仅起到了阻止普通消息A和C的作用,使得异步消息E能够优先执行。

7.总结

MessageQueue.next() 方法在有屏障消息和没有屏障消息时的处理流程存在显著区别:

  • 没有屏障消息时

    • 消息按照时间戳和类型(异步消息优先)顺序执行。
    • 异步消息即使在队列中后于普通消息,也会优先处理。
  • 有屏障消息时

    • 屏障消息阻止普通消息的处理,仅允许异步消息通过。
    • 当遇到屏障消息,next() 方法会跳过普通消息,优先查找并处理异步消息。
    • 屏障消息确保关键任务(如UI绘制)能够及时执行,避免被普通消息阻塞。

理解 MessageQueue.next() 方法在不同情况下的执行流程,有助于开发者更有效地管理消息队列,优化应用性能,提升用户体验。在实际开发中,合理使用异步消息和屏障消息,可以显著改善应用的响应速度和流畅度。

标签:异步,屏障,next,Handler,消息,msg,null
From: https://blog.csdn.net/fulai00/article/details/144023439

相关文章

  • 【通过错误消息DEBUG定位到增强】
    运行程序进入调试模式,自动跳转到调试页面,创建监控点按F8定位至消息报错的位置定位成功,点击程序事件按钮修改增强处代码......
  • 消息队列-kafka
    消息队列-kafkakafka常见面试题kafka实践环境准备代码结果截图参考摘要:本文将会对kafka进行介绍,首先介绍消息队列的一些基础知识,然后是kafka的基本概念和底层原理,以及kafka如何保证消息可靠性、消息不丢失,如何解决消息重复以及消息积压等问题,并且分析kafka为什么具......
  • 用rabbitmqadmin 模拟消息的创建、发布、订阅
    前言rabbitmqadmin工具可以方便地管理RabbitMQ的资源,包括创建交换机Exchanges、队列Queues、绑定Bindings,以及发布Publish和订阅Subscribe消息。确保你已经下载并安装了rabbitmqadmin,并且RabbitMQ管理插件是启用的。你可以从http://localhost:15672/cli/下载......
  • 再谈Windows消息循环
    一、什么是Windows消息循环概念介绍在Windows操作系统中,消息循环是应用程序处理消息的核心机制。消息是Windows应用程序与操作系统以及应用程序内部不同组件之间通信的基本单元。这些消息可以是由用户操作产生的,如鼠标点击、键盘按键;也可以是系统内部产生的,如窗口大小改变......
  • 直播-消息架构
    3、直播消息的技术特征在直播业务中,有几个关于消息模型的核心概念,我们先简单地总结一下,方便大家对直播相关的消息模型有一个整体上的理解。3.1实体关系直播系统消息模块对应的实体就是主播和观众。主播和观众:对于IM系统来说,都是普通用户,都会有一个唯一用户标识(用户ID),它也是IM分......
  • 功能最全的在线客服源码-独立链接-自动回复-消息提醒与手机接待回复等
    在当今数字化时代,客户服务已成为企业竞争的关键要素之一。一个高效、稳定且用户友好的在线客服聊天系统,不仅能提升客户满意度,还能有效提高企业运营效率。本文将详细介绍如何使用Golang开发一个美观且强大的在线客服聊天系统,并提供二进制运行的傻瓜式安装教程,帮助企业快速搭建自己......
  • 不同源H5页面消息通信(web-view 内的H5向父级页面发消息并回传结果给子页面)
    文章目录父级H5页面子级H5页面应用场景是两个H5不同源的情况下实现消息互通。本示例使用uniapp开发H5,父级H5通过<web-view>组件加载子H5页面;子H5页面向父级H5发送消息调起父级H5页面的微信扫一扫功能,再将扫一扫结果回传给子H5页面。父级H5链......
  • SseEmitter 服务器向客户端推送消息
    importorg.springframework.http.MediaType;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;importorg.springframework.web.servlet.mvc.method.annotation.SseEmitter;importjava.io.IOExc......
  • LabVIEW 队列消息处理器设计
    LabVIEW队列消息处理器设计队列消息处理器升级模式详解队列状态机学习概述队列状态机函数获取队列引用函数元素入队列函数元素出队列函数队列最前端插入元素函数释放队列引用函数队列消息处理器基本结构队列消息处理器升级模式详解当我们需要动态地根据用户的输入......
  • 消息中间件面试题之RocketMQ
    为什么使用消息队列?解耦、异步、削峰消息队列有什么优点和缺点?优点:解耦、异步、削峰缺点:系统的可用性降低、系统的复杂性提高了、一致性问题。RabbitMQ上的一个queue中存放的message是否有数量限制?限制是多少默认情况下一般是无限制,因为限制取决于机器的内存,但是消息过多会......