当前位置: 首页 > 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-…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...