【Android】Handler(四)Looper的相关知识点
Handler 机制是 Android 多线程间通信的一种常见方式。每个 Handler 对象由一个 Looper 和一个 MessageQueue 组成,用于将 Message 对象处理到指定的线程中。通过创建 Handler 实例,在子线程中创建 Message 对象并通过sendMessage()方法发送给 Handler,然后 Handler 会接收到消息并处理它。最后,Handler 在处理完消息后会将消息回传至主线程,再通过dispatchMessage()方法交由UI线程处理。
Looper 中在每一个线程上只有一个
ThreadLocal
线程是一个程序中执行的一条执行路径。每个线程都拥有自己的栈空间和寄存器等资源,因此它们之间是互相独立的。为了避免在多线程环境下出现资源竞争、数据不一致等问题,需要对线程之间的访问进行隔离和控制。其中一个解决方案就是使用 ThreadLocal。
ThreadLocal 是一个 Java 中的类,它可以在每个线程中存储和获取与其他线程隔离的变量值。具体来说,每个 ThreadLocal 对象都会存储到当前线程的 ThreadLocalMap 中。ThreadLocalMap 是一个以 ThreadLocal 对象为键、以变量值为值的 Map,它可以快速地访问和获取每个线程所拥有的 ThreadLocal 变量的值。当一个线程结束时,它所持有的 ThreadLocalMap 也会随之销毁。
在 Android 中,Looper 是一个消息循环机制,它可以让线程在消息队列中等待并处理消息。每个 Looper 对象都对应一个消息队列(MessageQueue)和一个函数(loop()),它可以不断地从消息队列中取出消息进行处理。
当我们调用 Looper.prepare() 方法时,该方法会为当前线程创建一个新的 Looper 对象。在 prepare() 方法内部,Looper 会将当前线程的 Looper 对象存储在一个名为 sThreadLocal 的 static 变量中。这个变量是一个 ThreadLocal 对象,这个变量是一个 ThreadLocal 对象,它可以在每个线程中存储和获取与其他线程隔离的变量值。由于 sThreadLocal 是一个 static 变量,所以它在整个应用程序中只存在一个实例,但是每个线程都可以通过这个变量来访问和管理自己的 Looper 对象。
具体来说,sThreadLocal 变量会存储到当前线程的 Thread 类中的 ThreadLocalMap 中。当我们需要使用某个线程的 Looper 对象时,只需要调用 Looper.myLooper() 方法即可获取当前线程所对应的 Looper 对象。这个方法会先获取当前线程的 ThreadLocalMap,然后从中获取到 sThreadLocal 变量对应的值,也就是当前线程的 Looper 对象。
通过使用 sThreadLocal 变量和 ThreadLocalMap,Looper 可以实现多线程之间的隔离和独立,确保每个线程都有自己的 Looper 对象。这个机制是 Android 框架中异步消息传递机制的核心之一,也是实现各种异步操作的基础。
Looper 的阻塞
Looper 中有两个方面的阻塞:
Message 不到时间,空转等待:
在某些情况下,Message 还没到执行时间之前,Looper 会一直进行循环,不断地空转等待,直到 Message 到了执行时间才继续执行。这种情况下,Looper 并没有真正的阻塞,它只是在等待 Message 到来。
MessageQueue 为空,阻塞等待:
如果 MessageQueue 中没有消息,那么 Looper 会进入阻塞状态,等待新的消息到来。这个过程中,Looper 对 CPU 的占用率非常低,因此不会对系统性能产生过大的影响。
需要注意的是,第二种情况可能会导致线程进入无限等待状态,从而造成应用程序的假死或 ANR(Application Not Responding)错误。
Looper设计模式
Looper 使用了生产者-消费者设计模式,其中 MessageQueue 充当生产者,Looper 的 loop() 方法充当消费者。
具体来说,MessageQueue 维护了一个消息队列,消息队列中的消息相当于生产者生产出来的产品,Looper 对消息队列进行消费和处理。在这个过程中,MessageQueue 和 Looper 之间是解耦的,它们可以独立地进行操作和管理。
当 MessageQueue 中产生新的消息时,它会将消息加入到消息队列的尾部,并通知 Looper 轮询消息队列。Looper 取到消息后,会依次对每个消息进行分发和执行,直到消息队列为空。整个过程中,MessageQueue 和 Looper 都不需要关心对方的具体实现细节,只需要按照约定好的协议进行数据传输和处理。
这种设计模式的优点是可以有效地降低耦合度,并提高代码的可读性和维护性。通过将生产者和消费者分离,我们可以更灵活地添加或修改消息的生产和消费方式,而无需对整个系统进行大规模的修改。同时,在多线程环境下,生产者和消费者之间的协作也可以有效避免竞争和冲突,保证了系统的线程安全性。
synchronized
Looper 中使用了 synchronized 关键字来实现线程之间的同步。具体来说,Looper 中的 loop() 方法和 MessageQueue 中的 enqueueMessage() 方法都是加了 synchronized 关键字的方法,它们在执行时都会获取 MessageQueue 对象的锁,确保同一时刻只能有一个线程访问和修改 MessageQueue。
这种同步机制是为了避免多个线程同时操作 MessageQueue 时产生的竞争和冲突。在 Android 系统中,生产者和消费者之间是通过 Message 消息进行通信的,因此必须保证 MessageQueue 中的消息能够正确地被分发和执行。如果没有同步机制,就会出现多个线程同时向 MessageQueue 中添加消息或者同时取出并处理消息的情况,从而导致数据不一致性和程序崩溃等问题。
例如:
Looper.prepare()
方法中创建和初始化 Looper 对象时,会加锁。由于每个线程只有一个 Looper 实例,因此需要确保在创建新的 Looper 实例的同时,不会出现多个线程同时执行该方法的情况。
Looper.loop()
方法中执行消息循环时,会加锁。该方法会不断地从 MessageQueue 中取出消息进行分发和执行,如果不加锁就会出现多个线程同时访问和修改 MessageQueue 的情况,从而导致数据一致性和程序错误。
MessageQueue.enqueueMessage()
方法中添加消息到 MessageQueue 时,会加锁。该方法会将新消息添加到 MessageQueue 的尾部,并通知正在等待消息的线程有新的消息可用。如果不加锁就会出现多个线程同时向 MessageQueue 添加消息的情况,可能导致消息顺序错乱或者丢失。
MessageQueue.next()
方法中获取下一个要处理的消息时,会加锁。该方法会从 MessageQueue 的头部取出下一个要处理的消息,并返回给调用者。如果不加锁就会出现多个线程同时取出并执行同一条消息的情况,可能导致数据的不一致性和程序的错误。
Looper 使用 synchronized 原因:
执行效率:
synchronized 是 Java 中最基本和常用的同步机制,由 JVM 内部实现,可以比较方便地保证线程安全。相对于其他的锁实现,synchronized 的执行效率相对较高,避免了过多的性能开销。
粒度控制:
在 Looper 的实现中,使用 synchronized 关键字可以比较方便地控制锁的粒度,避免了锁定过大的代码块或方法,从而提高了并发性能。如果使用其他类型的锁,可能会存在锁竞争或死锁,影响程序正确性和性能。
可重入性:
synchronized 是可重入锁,即一个线程在持有锁的情况下还可以重复获取锁,而不会出现死锁或其他的异常。在 Looper 中,需要实现消息循环并处理消息时可能需要多次进入锁定代码块或方法,因此使用可重入锁可以避免代码逻辑出错。
synchronized 是 Java 中最基本和常用的同步机制,而且在大多数情况下可以提供良好的并发性能和可靠性。对于轻量级的同步需求,synchronized 是不错的选择。
Handler线程间通信机制通过什么实现
Handler 是一种消息处理机制,它提供了一种在不同线程之间进行通信的方式。由于·Handler 是在同一个进程中创建的,因此它们可以共享进程中的内存,从而实现线程间的通信
。
当我们在一个线程中创建 Handler 时,这个 Handler 会与当前线程中的 Looper 绑定,并创建一个 MessageQueue 对象。然后,我们可以使用这个 Handler 向该线程中的 MessageQueue 发送消息(即构造 Message 对象并添加到该队列中),这些消息就会被 Looper 接收并交给对应的 Handler 进行处理。
当我们需要在其他线程中发送消息时,就需要使用 Handler 的 post 方法或者 sendMessage 方法,这些方法会将要发送的消息封装成一个 PendingMessage 对象,并加入到目标线程的消息队列中。最终,目标线程中的 Looper 会将这个 PendingMessage 对象转化成 Message 对象,并交给绑定的 Handler 进行处理。
相关文章:
【Android】Handler(四)Looper的相关知识点
Handler 机制是 Android 多线程间通信的一种常见方式。每个 Handler 对象由一个 Looper 和一个 MessageQueue 组成,用于将 Message 对象处理到指定的线程中。通过创建 Handler 实例,在子线程中创建 Message 对象并通过sendMessage()方法发送给 Handler&a…...

Redis缓存雪崩及解决办法
缓存雪崩 1.缓存雪崩是指在同- -时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到 达数据库,带来巨大压力。 2.解决方案: ◆给不同的Key的TTL添加随机值 ◆利用Redis集群提高服务的可用性 ◆给缓存业务添加降级限流策略 降级可做为系统的保底…...

Maven私服仓库配置-Nexus详解
目录 一、什么是Maven私服?二、Maven 私服优势三、Maven 私服搭建四、Sonatype Nexus介绍五、Nexus仓库属性和分类六、Nexus仓库配置以及创建仓库七、Nexus配置用户角色八、Maven SNAPSHOT(快照)九、项目当中配置Nexus上传依赖十、项目当中配置Nexus下载依赖十一、测…...
Systrace系列10 —— Binder 和锁竞争解读
本文主要是对 Systrace 中的 Binder 和锁信息进行简单介绍,简单介绍了 Binder 的情况,介绍了 Systrace 中 Binder 通信的表现形式,以及 Binder 信息查看,SystemServer 锁竞争分析等。 Binder 概述 Android 的大部分进程间通信都使用 Binder,这里对 Binder 不做过多的解释…...
React Hooks中使用useState异步回调获取不到最新值的问题
ReactHook中useState异步回调获取不到最新值及解决⽅案 预先了解 setState 的两种传参⽅式 1、直接传⼊新值 setState(options); 列如: const [state, setState] useState(0); setState(state 1); 2、传⼊回调函数 setState(callBack); 例如: …...
JavaScript 高级 (完结)
目录 深浅拷贝 浅拷贝 深拷贝 递归实现深拷贝 js库lodash里面cloneDeep内部实现了深拷贝 JSON序列化 异常处理 throw 抛异常 try /catch 捕获异常 debugg 处理this this指向 普通函数 箭头函数 改变this call() apply() bind() call apply bind 总结 性能优化…...

【P30】JMeter 事务控制器(Transaction Controller)
文章目录 一、事务控制器(Transaction Controller)参数说明二、测试计划设计2.2.1、勾选 Generate parent sample2.2.1、勾选 Include duration of timer and pre-post processors in generated sample 一、事务控制器(Transaction Controlle…...

【MySQL】MySQL的事务原理和实现?
文章目录 MySQL事务的底层实现原理一、事务的目的可靠性和并发处理 二、实现事务功能的三个技术2.1 redo log 与 undo log介绍2.1.1 redo log2.1.2undo log 2.2 mysql锁技术2.2.1 mysql锁技术 2.3 MVCC基础 三、事务的实现3.1 原子性的实现3.1.1 undo log 的生成3.1.2 根据undo…...
S7-300Smart1200的ISO on TCP通信
1、西门子PLC的通信资源 1.1 S7-1200 的PROFINET 通信口 S7-1200 CPU 本体上集成了一个 PROFINET 通信口,支持以太网和基于 TCP/IP 的通信标准。使用这个通信口可以实现 S7-1200 CPU 与编程设备的通信,与HMI触摸屏的通信,以及与其它 CPU 之间的通信。这个PROFINET 物理接口…...

Spark写入Hive报错Mkdir failed on :com.alibaba.jfs.JindoRequestPath
1. 报错内容 23/05/31 14:32:13 INFO [Driver] FsStats: cmdmkdirs, srcoss://sync-to-bi.[马赛克].aliyuncs.com/tmp/hive, dstnull, size0, parameterFsPermission:rwx-wx-wx, time-in-ms32, version3.5.0 23/05/31 14:32:13 ERROR [Driver] ApplicationMaster: User class …...

分布式id解决方法--雪花算法
uuid,jdk自带,但是数据库性能差,32位呀。 mysql数据库主键越短越好,Btree产生节点分裂,大大降低数据库性能,所以uuid不建议。 redis的自增,但是要配置维护redis集群,就为了一个id&a…...

5年经验之谈:月薪3000到30000,测试工程师的变“行”记
自我介绍下,我是一名转IT测试人,我的专业是化学,去化工厂实习才发现这专业的坑人之处,化学试剂害人不浅,有毒,易燃易爆,实验室经常用丙酮,甲醇,四氯化碳,接触…...

PMP考试都是什么题?
PMP新版大纲加入了ACP敏捷管理的内容,说是敏捷混合题型占到了 50%,但是这次318的考试,敏捷题占了大半,都说敏捷和情景快要占到80%-90%。 所以有友友说开了四个小时盲盒,题目读不懂,或者觉得4个选项都不对或…...

macbook2023系统清理软件cleanmymac中文版
cleanmymac x 中文版基本都是大家首选Mac清理软件了。它集各种功能于一身,几乎满足用户所有的清理需求。它可以清理,优化,保养和监测您的电脑,确保您的Mac运行畅通无阻!支持一键快速清理Mac,快速检查并安全…...

基于Python+AIML+Tornado的智能聊天机器人(NLP+深度学习)含全部工程源码+语料库 适合个人二次开发
目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境Tornado 环境 模块实现1. 前端2. 后端3. 语料库4. 系统测试 其它资料下载 前言 本项目旨在利用AIML技术构建一个聊天机器人,实现用户通过聊天界面与机器人交互的功能。通过提供的工程源代码…...
算法Day15 | 层序遍历,102,107,199,637,429,515,116,117,104,111,226,101
Day15 层序遍历102.二叉树的层序遍历107.二叉树的层次遍历 II199.二叉树的右视图637.二叉树的层平均值429.N叉树的层序遍历515.在每个树行中找最大值116.填充每个节点的下一个右侧节点指针117.填充每个节点的下一个右侧节点指针II104.二叉树的最大深度111.二叉树的最小深度 226…...

Prometheus+Grafana学习(十一)安装使用pushgateway
Pushgateway允许短暂和批量作业将其指标暴露给 Prometheus。由于这些工作的生命周期可能不足够长,不能够存在足够的时间以让 Prometheus 抓取它们的指标。Pushgateway 允许它们可以将其指标推送到 Pushgateway,然后 Pushgateway 再将这些指标暴露给 Prom…...
深入理解C/C++预处理器指令#pragma once以及与ifndef的比较
#pragma once用法总结 为了防止重复引用造成二义性 在C/C中,在使用预编译指令#include的时候,为了防止重复引用造成二义性,通常有两种方式 第一种是#ifndef指令防止代码块重复引用,比如说 #ifndef _CODE_BLOCK #define _CODE_BLO…...

git 环境配置 + gitee拉取代码
好嘛 配环境的时候 老是忘记这个命令行 干脆自己写一个记录一下 也不用搜了 1.先从git官网下载git 安装 2.然后从gitee拉取代码的时候提示 这是因为换了新电脑没有加入新的公钥啦 哎 所以老是记不住命令行 first : git config --global user.name “Your Name” …...

港联证券|港股拥抱特专科技企业 内资券商“修炼内功”蓄势而为
港股市场新一轮改革举措渐次落地。特别是港交所推出特专科技公司上市机制,吸引符合资格的科技企业申请赴港上市,成为这一轮港股市场改革的“重头戏”。 作为香港资本市场的重要参与者,内资券商立足香港、背靠内地、辐射全球,走出一…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...

MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...
React父子组件通信:Props怎么用?如何从父组件向子组件传递数据?
系列回顾: 在上一篇《React核心概念:State是什么?》中,我们学习了如何使用useState让一个组件拥有自己的内部数据(State),并通过一个计数器案例,实现了组件的自我更新。这很棒&#…...