【17】Android 线程间通信(二) - Handler

概述

记得上篇文章我们留下的问题吗?如果还没有看过上一篇讲解Handler基本原理文章的同学可以补一下知识。Android 线程间通信(一) - Handler

回到正题,这次我们将一下上一篇文章留下问题中的几个,idleHandler & syncBarrier。

同步屏障

首先我们了解吗,我们平时通过Handler#sendMessage发送的属于什么消息?同步消息还是异步消息。既然这次讲到同步屏障,我们也可以大胆的猜想,我们平时发送的消息是同步消息,只有特殊情况下,插入这个同步屏障,才会走特殊的异步方式。
如果能够这样推导,说明在Handler这一块的了解已经比较深刻了。我们继续看一下源码。

	//MessageQueue.java@UnsupportedAppUsageMessage next() {...int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {// Try to retrieve the next message.  Return if found.final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;//(1) msg不为空,且msg.target为空才会进入if (msg != null && msg.target == null) {// Stalled by a barrier.  Find the next asynchronous message in the queue.do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}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;}...}

我们看代码中(1)注解的位置,msg不为空这个很好理解,消息需要有实体,但是target为空这个是为什么呢?上一片文章讲过,msg的target其实就是Handler。而下面官方的注解说明,被栏栅挡住了,在队列中查找下一个异步消息。然后开始从当前这个msg的下一个msg开始查找,知道下一个消息属于异步消息或者查找直到为空位置,跳出循环。

可见挡住同步消息的,正是这个msg.target=null的消息,那么它是怎么插入进来的呢?我们接着看。

	//MessageQueue.java@UnsupportedAppUsage@TestApipublic int postSyncBarrier() {return postSyncBarrier(SystemClock.uptimeMillis());}private int postSyncBarrier(long when) {// Enqueue a new sync barrier token.// We don't need to wake the queue because the purpose of a barrier is to stall it.synchronized (this) {final int token = mNextBarrierToken++;final Message msg = Message.obtain();msg.markInUse();msg.when = when;msg.arg1 = token;Message prev = null;Message p = mMessages;if (when != 0) {while (p != null && p.when <= when) {prev = p;p = p.next;}}if (prev != null) { // invariant: p == prev.nextmsg.next = p;prev.next = msg;} else {msg.next = p;mMessages = msg;}return token;}}

MessageQueue中有一个方法叫做postSyncBarrier,可以理解为插入同步屏障。首先通过Message#obtain获取消息,这里是对消息对象的一个复用,在消息处理完之后,会把Message的target清除。然后标记为使用,接着就是消息的一些属性赋值,但唯独没有重新确定msg的target对象。可见如果messageQueue需要插入同步屏障,就是通过这个方法把屏障消息插入进去的。

插入同步屏障消息是直接通过MessageQueue进行插入

既然有插入同步屏障,那么也有移除屏障,让同步消息可以继续执行。

	//MessageQueue.java@UnsupportedAppUsage@TestApipublic void removeSyncBarrier(int token) {// Remove a sync barrier token from the queue.// If the queue is no longer stalled by a barrier then wake it.synchronized (this) {Message prev = null;Message p = mMessages;while (p != null && (p.target != null || p.arg1 != token)) {prev = p;p = p.next;}if (p == null) {throw new IllegalStateException("The specified message queue synchronization "+ " barrier token has not been posted or has already been removed.");}final boolean needWake;if (prev != null) {prev.next = p.next;needWake = false;} else {mMessages = p.next;needWake = mMessages == null || mMessages.target != null;}p.recycleUnchecked();// If the loop is quitting then it is already awake.// We can assume mPtr != 0 when mQuitting is false.if (needWake && !mQuitting) {nativeWake(mPtr);}}}

还有个问题,就是同步屏障是挡住同步消息,那么怎么发送异步消息呢?我们看看Message的源码。

	// Message.javapublic void setAsynchronous(boolean async) {if (async) {flags |= FLAG_ASYNCHRONOUS;} else {flags &= ~FLAG_ASYNCHRONOUS;}}

有一个设置异步的方法,根据传入的bool值,flags和标志位做位运算。我们只要了解,当async为true,flags = 1,async为false,flags = 0。之前跳出next中的do…while循环中,判断异步消息的方式,就是msg#isAsynchironous。也就是说,setAsynchronous当传入true的时候,判断消息的条件就为真,此时发送的消息就是异步消息了。

	//Message.javapublic boolean isAsynchronous() {return (flags & FLAG_ASYNCHRONOUS) != 0;}

默认情况下flags = 0,为同步消息

这样一来,同步屏障的作用和使用方式,我们就彻底搞懂了。

IdleHandler

空闲Handler,和字面意思一样。Handler是消息处理的句柄,那么IdleHandler,就是处理空闲消息的句柄。在MessageQueue#next源码中,我们继续看一下。

	//MessageQueue#next// If first time idle, then get the number of idlers to run.// Idle handles only run if the queue is empty or if the first message// in the queue (possibly a barrier) is due to be handled in the future.if (pendingIdleHandlerCount < 0&& (mMessages == null || now < mMessages.when)) {pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount <= 0) {// No idle handlers to run.  Loop and wait some more.mBlocked = true;continue;}if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}// Run the idle handlers.// We only ever reach this code block during the first iteration.for (int i = 0; i < pendingIdleHandlerCount; i++) {final IdleHandler idler = mPendingIdleHandlers[i];mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean keep = false;try {keep = idler.queueIdle();} catch (Throwable t) {Log.wtf(TAG, "IdleHandler threw exception", t);}if (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}// Reset the idle handler count to 0 so we do not run them again.pendingIdleHandlerCount = 0;// While calling an idle handler, a new message could have been delivered// so go back and look again for a pending message without waiting.nextPollTimeoutMillis = 0;}

next方法下面有这样一段代码,什么时候会执行下去呢?就是当msg=null的时候,因为当msg不为空,说明取出了msg,并返回给Looper进行后续分发了。msg为null,说明此时消息队列中取不出消息了,就会从mIdlehandlers这个集合中,获取集合大小,并且把集合转换成数组mPendingIdleHandlers。

接着通过一个for循环,取出每一个idler并执行它的queueIdle,这个queueIdle是一个接口,可见是一个接口回调的操作,回调给用户去操作了。

	//MessageQueue.javapublic void addIdleHandler(@NonNull IdleHandler handler) {if (handler == null) {throw new NullPointerException("Can't add a null IdleHandler");}synchronized (this) {mIdleHandlers.add(handler);}}

通过MessageQueue的addIdleHandler接口,我们可以向MessageQueue维护的集合中添加这种空闲Handler,当消息队列MessageQueue的消息队列中没有任务可以执行之后,就会执行这些空闲的消息。

可以发现,IdleHandler适合处理那些优先级比较低的事情,而不会影响到用户界面的响应性。避免在主线程繁忙的时候执行耗时任务,从而保持应用的流畅性。

总结

1、MessageQueue可以发送同步屏障消息
2、Message可以通过设置确认发送的消息为同步还是异步
3、同步屏障消息的Handler为空
4、MessageQueue可以添加IdleHandler来处理优先级较低的任务

这下应用层Handler的东西我们都了解了。下一篇文章接下来将通过native层来分析一下Handler这个线程间通信的方式,底层到底还做了什么。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/872893.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PyCharm查看文件或代码变更记录

背景&#xff1a; Mac笔记本上有一个截图的定时任务在运行&#xff0c;本地Python使用的是PyCharm IDE&#xff0c;负责的同事休假&#xff0c;然后定时任务运行的结果不符合预期&#xff0c;一下子不知道问题出现在哪里。 定位思路&#xff1a; 1、先确认网络、账号等基本的…

Qt 快速保存配置的方法

Qt 快速保存配置的方法 一、概述二、代码1. QFileHelper.cpp2. QSettingHelper.cpp 三、使用 一、概述 这里分享一下&#xff0c;Qt界面开发时&#xff0c;快速保存界面上一些参数配置的方法。 因为我在做实验的时候&#xff0c;界面上可能涉及到很多参数的配置&#xff0c;我…

实战打靶集锦-31-monitoring

文章目录 1. 主机发现2. 端口扫描3. 服务枚举4. 服务探查4.1 ssh服务4.2 smtp服务4.3 http/https服务 5. 系统提权5.1 枚举系统信息5.2 枚举passwd文件5.3 枚举定时任务5.4 linpeas提权 6. 获取flag 靶机地址&#xff1a;https://download.vulnhub.com/monitoring/Monitoring.o…

django学习入门系列之第四点《BootStrap依赖》

文章目录 往期回顾 BootStrap依赖于JavaScript的类库&#xff0c;JQuery下载 下载JQuery&#xff0c;在界面上应用JQuery 在页面上应用BootStrap和avaScript的类库【JQuery是avaScript的类库】 JQuery的官网&#xff1a; jQuery 如果要应用JQuery 则要在body里面导入文件…

[Java后端面试题1]2024-07-16

TIME_WAIT与CLOSE_WAIT的区别 TIME_WAIT 和 CLOSE_WAIT 是 TCP 连接状态的一部分&#xff0c;用于描述连接在不同阶段的状态。它们有以下主要区别&#xff1a; TIME_WAIT 定义: 这是主动关闭连接的一方在发送了最后一个 ACK 后进入的状态。出现原因: 确保远程 TCP 协议收到最…

react中状态管理useState

[secret,setSecret]useState(null)useEffect(() > {http.get(/api/suggest/qrcode/, { params: { uniqueid: store.uniqueid } }).then(res > {//异步获取的值不是实时返回的setSecret(res.secret);return http.get(/api/suggest/userinfo/, { params: { secret: secret …

Mysql8.4参考手册走读(六)

MySQL 8.4 常见问题解答&#xff1a;服务器 SQL 模式 问题答案什么是服务器 SQL 模式&#xff1f;服务器 SQL 模式定义了 MySQL 应支持的 SQL 语法和它应该执行哪种类型的数据验证检查。这使在不同环境中使用 MySQL 变得更加容易&#xff0c;并且将 MySQL 与其他数据库服务器一…

MySQL(7)内外连接+索引

目录 1.内外连接; 2. 索引; 1.内外连接: 1.1内连接: 语法: select 字段 from 表名 inner join 表名 on 字段限制; 1.2 外连接: 分为左右外连接; (1)左外连接: 语法: select * from 表名 left join 表名 on 字段限制. &#x1f330;查询所有学生的成绩&#xff0c;如果这个学生…

【C++】编程新思想,通过封装新的类创建新的数据类型

我们可以通过封装一个新的C++类来实现新的数据类型的定义。 目录 优点: 主要方法: show(); reset(); use(); change(); operator++(): operator++(int); operator--() ; self_increase(); self_reduction(); 完整代码: 优点: 用新的名字代替原有数据类型名…

什么才是好用的大模型?

随着大模型在千行百业的逐步落地&#xff0c;中国基础大模型正直面来自用户端的诸多拷问。 近日&#xff0c;随着OpenAI宣布禁止中国用户使用其API&#xff0c;更多的国产大模型都在提供替代方案和优惠措施&#xff0c;来吸引和支持开发者进行用户迁移。 这既是一次挑战&…

多多OJ评测系统 前端项目环境初始化 安装Vue脚手架 引入Arco Design组件

目录 确定环境 命令行输入 装一下脚手架 监测一下是否安装成功 创建一个项目 选择一系列的配置后 我们打开webStorm 配置脚手架后我们先运行 我们这边能获取到网址 其实我们脚手架已经帮我们做到了 接下来要引入相关的组件 选择用npm进行安装 我们建议的是完整引入…

UDP客户端、服务端及简易聊天室实现 —— Java

UDP 协议&#xff08;用户数据包协议&#xff09; UDP 是无连接通信协议&#xff0c;即在数据传输时&#xff0c;数据的发送端和接收端不建立逻辑连接&#xff0c;简单来说&#xff0c;当客户端向接收端发送数据时&#xff0c;客户端不会确认接收端是否存在&#xff0c;就会发出…

windows镜像下载网站

一个专注于提供精校、完整、极致的Windows系统下载服务的仓储站&#xff0c;网站托管于Github。 网站&#xff1a;https://hellowindows.cn/ 下载方式&#xff1a;ED2k&#xff0c;BT&#xff0c;百度网盘 MSDN - 山己几子木&#xff0c;提供Windows 11、Windows 10等不同版本…

单例模式Singleton

设计模式 23种设计模式 Singleton 所谓类的单例设计模式&#xff0c;就是采取一定的方法保证在整个的软件系统中&#xff0c;对某个类只能存在一个对象实例&#xff0c;并且该类只提供一个取得其对象实例的方法。 饿汉式 public class BankTest {public static void main(…

Qt模型/视图架构——委托(delegate)

一、为什么需要委托 模型&#xff08;model&#xff09;用来数据存储&#xff0c;视图&#xff08;view&#xff09;用来展示数据。因此&#xff0c;模型/视图架构是一种将数据存储和界面展示分离的编程方法。具体如下图所示&#xff1a; 由图可知&#xff0c;模型向视图提供数…

python 迭代器介绍 map() 函数

迭代器&#xff08;Iterator&#xff09;在Python中是一种可以记住遍历位置的对象。迭代器对象从集合的第一个元素开始访问&#xff0c;直到所有的元素被访问完为止。迭代器只能往前不会后退&#xff0c;也就是只能前进遍历&#xff0c;不能反向遍历或跳过元素。迭代器在Python…

oracle备份和恢复-----从全库备份中恢复用户库解题思路

文章目录 从全库备份中恢复用户库的思路备份全库恢复用户库 从全库备份中恢复用户库的思路 备份全库 第一步&#xff1a;使用系统用户将oracle整个导出备份----全库备份 1.exp system/system buffer65536 feedback100000 fully fileD:/exp.dmp logexp.log ---导出备份文件位…

响应式编程:Project Reactor与WebFlux

在现代Web应用程序中&#xff0c;响应式编程成为一种越来越流行的方法&#xff0c;以处理高并发和高吞吐量。响应式编程不仅仅是一种新的编程范式&#xff0c;它还引入了一套新的工具和技术来处理数据流和异步事件。本文将深入探讨Project Reactor与Spring WebFlux&#xff0c;…

3D问界—MAYA制作铁丝栅栏(透明贴图法)

当然&#xff0c;如果想通过建立模型法来实现铁丝栅栏的效果&#xff0c;也不是不行&#xff0c;可以找一下栅栏建模教程。本篇文章主要是记录一下如何使用透明贴图来实现创建铁丝栅栏&#xff0c;主要应用于场景建模&#xff0c;比如游戏场景、建筑场景等大环境&#xff0c;不…

PGCCC|【PostgreSQL】PG考证对工作上有什么好处# PG证书

认证 PostgreSQL 考证&#xff08;PostgreSQL Certification&#xff09;在工作上有以下几个好处&#xff1a; 增强专业能力&#xff1a;通过考证&#xff0c;可以系统地学习和掌握 PostgreSQL 数据库的知识和技能&#xff0c;提高自己的专业水平。 提升职业竞争力&#xff1…