当前位置: 首页 > news >正文

Android中级——消息机制

消息机制

  • 概念
  • ThreadLocal
  • MessageQueue
  • Looper
  • Handler
  • runOnUiThread()

概念

  • MessageQueue:采用单链表的方法存储消息列表
  • Looper:查询MessageQueue是否有新消息,有则处理,无则等待
  • ThreadLocal:用于Handler获取当前线程的Looper
  • 线程默认没有Looper,当使用Handler时需要手动创建,ActivityThread被创建时会初始化主线程的Looper,故主线程中默认可以使用Handler

ThreadLocal

ThreadLocal是线程内部数据存储类,通过它可以在指定线程中存储数据,且只能在该线程中访问,如在调用栈比较深时用于存储监听器,不需要全局变量即可实现监听线程的全部执行过程,且避免了监听器通过参数去传递

public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<>();protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mBooleanThreadLocal.set(true);Log.d(TAG, "onCreate: Thread#main mBooleanThreadLocal = " + mBooleanThreadLocal.get());new Thread("Thread#1"){@Overridepublic void run() {mBooleanThreadLocal.set(false);Log.d(TAG, "onCreate: Thread#1 mBooleanThreadLocal = " + mBooleanThreadLocal.get());}}.start();new Thread("Thread#2"){@Overridepublic void run() {Log.d(TAG, "onCreate: Thread#2 mBooleanThreadLocal = " + mBooleanThreadLocal.get());}}.start();}
}

打印如下,虽然不同线程使用同一个ThreadLocal对象,但值却不一样

Thread#main mBooleanThreadLocal = true
Thread#1 mBooleanThreadLocal = false
Thread#2 mBooleanThreadLocal = null

其set()方法如下,内部通过所传入线程threadLocals(ThreadLocalMap.Entry)存储数据

public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);
}ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);
}static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}
}

再看下set()的实际实现

private void set(ThreadLocal<?> key, Object value) {Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();if (k == key) {e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}tab[i] = new Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();
}

其get()方法如下,若存在则返回数据,不存在则创建ThreadLocalMap,存储默认值null并返回

public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {T result = (T)e.value;return result;}}return setInitialValue();
}private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value;
}protected T initialValue() {return null;
}

MessageQueue

如下为插入操作enqueueMessage(),主要为链表的插入

boolean enqueueMessage(Message msg, long when) {......synchronized (this) {......msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;if (p == null || when == 0 || when < p.when) {msg.next = p;mMessages = msg;needWake = mBlocked;} else {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;}}msg.next = p; prev.next = msg;}if (needWake) {nativeWake(mPtr);}}return true;
}

如下为读取操作,死循环遍历链表,在规定时间后将Message返回并从链表删除

Message next() {......for (; ; ) {......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;}} ......}......}
}

Looper

使用Handler需要Looper,否则会报错

在这里插入图片描述

可修改为如下

public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread("Thread#1") {@Overridepublic void run() {Looper.prepare();Looper looper = Looper.myLooper();Handler handler = new Handler();Looper.loop();}}.start();}
}

prepare()创建Looper(内部创建MessageQueue),并存储到ThreadLocal

public static void prepare() {prepare(true);
}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));
}private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();
}

loop()开启消息循环

  • 死循环一直从MessageQueue的next()方法获取Message
  • quit()、quitSafely()可让next()返回null退出Looper,前者直接退出,后者处理完MessageQueue中的已有消息后退出,退出后handler的send()返回false
  • 调用Handler的dispatchMessage(),回到创建Handler所在的Looper中执行
public static void loop() {final Looper me = myLooper();......final MessageQueue queue = me.mQueue;for (;;) {Message msg = queue.next();if (msg == null) {return;}......try {msg.target.dispatchMessage(msg);......}......}
}

Handler

sendMessage()将Message插入到MessageQueue

public final boolean sendMessage(@NonNull Message msg) {return sendMessageDelayed(msg, 0);
}public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);
}private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {msg.target = this;msg.workSourceUid = ThreadLocalWorkSource.getUid();if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);
}

dispatchMessage()方法

  • msg.callback即是传进来的Runnable,若不为空则调用其run()
  • 判断mCallback是否为空,不为空调用其handleMessage(),当不想继承Handler创建子类时,其用于创建匿名内部类
  • 否则调用子类的handleMessage()
 public void dispatchMessage(@NonNull Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}private static void handleCallback(Message message) {message.callback.run();}

runOnUiThread()

Activity中的runOnUiThread(),若当前线程是主线程,则直接执行,否则调用主线程创建的Handler中的post()

public final void runOnUiThread(Runnable action) {if (Thread.currentThread() != mUiThread) {mHandler.post(action);} else {action.run();}
}

相关文章:

Android中级——消息机制

消息机制 概念ThreadLocalMessageQueueLooperHandlerrunOnUiThread() 概念 MessageQueue&#xff1a;采用单链表的方法存储消息列表Looper&#xff1a;查询MessageQueue是否有新消息&#xff0c;有则处理&#xff0c;无则等待ThreadLocal&#xff1a;用于Handler获取当前线程的…...

【kubernetes】使用KubeSphere devops部署我的微服务系统

KubeSphere Devops 入门使用KubeSphere的Devops功能部署"我的微服务系统" &#xff08;内容学习于尚硅谷云原生课程&#xff09; kubesphere devops官方文档&#xff1a; https://v3-1.docs.kubesphere.io/zh/docs/devops-user-guide/how-to-use/create-a-pipeline-u…...

【哈士奇赠书活动 - 37期】- 〖深入浅出SSD:固态存储核心技术、原理与实战 第2版〗

文章目录 ⭐️ 赠书 - 《深入浅出SSD&#xff1a;固态存储核心技术、原理与实战 第2版》⭐️ 内容简介⭐️ 作者简介⭐️ 编辑推荐⭐️ 赠书活动 → 获奖名单 ⭐️ 赠书 - 《深入浅出SSD&#xff1a;固态存储核心技术、原理与实战 第2版》 ⭐️ 内容简介 本书从基础认知、核心技…...

25.CSS自定义形状按钮与悬停效果

效果 源码 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>CSS Custom Shape Button</title><link rel="stylesheet" href="style.css"> </head> <body&…...

两条速度相差1350倍的sql语句

一起来学习研究下&#xff0c;看看是什么导致的这么大差别&#xff1a; SQL1: select * from RESOURCES where RES_STATUS 1 and PLATFORM_FLAG1 and RES_ID in (select RES_ID from DOWNLOADRECORDSwhere DOWNLOADRECORDS_TIME > DATE_SUB(now(),INTERVAL 7 DAY) grou…...

【UniApp开发小程序】小程序首页完善(滑到底部数据翻页、回到顶端、基于回溯算法的两列数据高宽比平衡)【后端基于若依管理系统开发】

文章目录 说明细节一&#xff1a;首页滑动到底部&#xff0c;需要查询下一页的商品界面预览页面实现 细节二&#xff1a;当页面滑动到下方&#xff0c;出现一个回到顶端的悬浮按钮细节三&#xff1a;商品分列说明优化前后效果对比使用回溯算法实现ControllerService回溯算法 优…...

使用errors.Wrapf()代替log.Error()

介绍不同语言的错误处理机制: Error handling patterns[1] Musings about error handling mechanisms in programming languages[2] 项目中 main调func1&#xff0c;func1调取func2... 这样就会出现很多的 if err ! nil { log.Printf()} , 在Kibana上查看时会搜到多条日志, 需要…...

企业面临的IP风险,如何应对?

IP风险画像为企业或组织在知识产权领域面临的潜在风险和威胁的综合概览。通过对相关知识产权的保护和管理&#xff0c;企业可以预测和应对潜在的法律、商业和声誉风险。 IP数据云帮助企业更好地了解和应对知识产权方面的风险。并提供了关于当前全球知识产权环境的重要信息&…...

L1-046 整除光棍(Python实现) 测试点全过

题目 这里所谓的“光棍”&#xff0c;并不是指单身汪啦~ 说的是全部由1组成的数字&#xff0c;比如1、11、111、1111等。传说任何一个光棍都能被一个不以5结尾的奇数整除。比如&#xff0c;111111就可以被13整除。 现在&#xff0c;你的程序要读入一个整数x&#xff0c;这个整…...

《Web安全基础》04. 文件上传漏洞

web 1&#xff1a;文件上传漏洞2&#xff1a;WAF 绕过2.1&#xff1a;数据溢出2.2&#xff1a;符号变异2.3&#xff1a;数据截断2.4&#xff1a;重复数据 本系列侧重方法论&#xff0c;各工具只是实现目标的载体。 命令与工具只做简单介绍&#xff0c;其使用另见《安全工具录》…...

文本匹配实战系列

引言 本系列文章开始介绍深度学习在文本匹配领域的应用&#xff0c;并且会尝试得到各种模型在给定的数据集上的表现。 深度文本匹配发展比较久&#xff0c;积累了很多文本匹配方法。也有很多的分类方式&#xff0c;一种分类方式是表示型和交互型。 表示型方法 表示型(repre…...

【Kafka】Kafka Stream简单使用

一、实时流式计算 1. 概念 一般流式计算会与批量计算相比较。在流式计算模型中&#xff0c;输入是持续的&#xff0c;可以认为在时间上是无界的&#xff0c;也就意味着&#xff0c;永远拿不到全量数据去做计算。同时&#xff0c;计算结果是持续输出的&#xff0c;也即计算结果…...

在Linux服务器上,查看系统最近的重启记录

在Linux服务器上&#xff0c;您可以查看系统的重启记录以了解系统何时进行了重启。系统的重启记录通常被记录在系统日志文件中。以下是在不同Linux发行版上查看系统重启记录的方法&#xff1a; 1. 使用 last 命令&#xff1a; 打开终端&#xff0c;并输入以下命令来查看系统的…...

Vue2023 面试归纳及复习

1. Vue 3中的Composition API&#xff08;Hooks&#xff09;是什么&#xff1f;它与Options API有何不同&#xff1f; 答&#xff1a;Composition API是Vue 3中引入的一种新的API风格&#xff0c; 用于组织和重用组件逻辑。它与Options API相比&#xff0c; 提供了更灵活和可…...

Android动态可编辑长度列表

概述 在界面实现一个列表&#xff0c;用户可以随意给列表新增或者删除项目&#xff0c;在开发中比较常用&#xff0c;但是真正做起来又有点花时间&#xff0c;今天花时间做一个&#xff0c;以便在以后的开发中用到。 详细 运行效果&#xff1a; 二、实现思路&#xff1a; 1…...

合并对象在 Typescript 中的实现与应用

合并对象在 Typescript 中的实现与应用 文章目录 合并对象在 Typescript 中的实现与应用一、简介二、实现1、函数实现2、参数说明3、返回值 三、使用示例四、实际应用场景五、拓展&#xff1a;使用 lodash-es 的 assign 函数进行对象合并1、简介2、安装与导入3、基础用法4、注意…...

antd upload组件beforeUpload返回promise之后,获取的文件不是file类型导致上传失败

之前的beforeUpload直接返回一个false值 &#xff0c;文件是可以正常与服务端进行传输的 beforeUpload: (file) > {return false},但是这样并不能阻止文件上传&#xff0c;看了官方文档后&#xff0c;改用返回promise对象上传 beforeUpload: (file) > {console.log(-befo…...

创建ffmpeg vs2019工程

0 写在前面 本文主要参考链接&#xff1a;https://www.cnblogs.com/suiyek/p/15669562.html 感谢作者的付出&#xff1b; 1 目录结构 2 下载yasm和nasm 如果自己在安装VS2019等IDE的时候已经安装了它们&#xff0c;则不用再单独进行安装&#xff0c;比如我这边已经安装了&a…...

无涯教程-机器学习 - Jupyter Notebook函数

Jupyter笔记本基本上为开发基于Python的数据科学应用程序提供了一个交互式计算环境。它们以前称为ipython笔记本。以下是Jupyter笔记本的一些功能,使其成为Python ML生态系统的最佳组件之一- Jupyter笔记本可以逐步排列代码,图像,文本,输出等内容,从而逐步说明分析过程。 它有…...

ubuntu安装单机的Consul

文章目录 场景解决启动方式 场景 公司使用Consul做注册发现中心以及管理配置&#xff0c;之前没有用过consul, 现在记录下ubuntu部署的过程 解决 apt 安装 wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...