鸿蒙OS 线程间通信
鸿蒙OS 线程间通信概述
在开发过程中,开发者经常需要在当前线程中处理下载任务等较为耗时的操作,但是又不希望当前的线程受到阻塞。此时,就可以使用 EventHandler 机制。EventHandler 是 HarmonyOS 用于处理线程间通信的一种机制,可以通过 [EventRunner] 创建新线程,将耗时的操作放到新线程上执行。这样既不阻塞原来的线程,任务又可以得到合理的处理。比如:主线程使用 EventHandler 创建子线程,子线程做耗时的下载图片操作,下载完成后,子线程通过 EventHandler 通知主线程,主线程再更新 UI。
基本概念
EventRunner 是一种事件循环器,循环处理从该 EventRunner 创建的新线程的事件队列中获取 InnerEvent 事件或者 Runnable 任务。InnerEvent 是 EventHandler 投递的事件。
EventHandler 是一种用户在当前线程上投递 InnerEvent 事件或者 Runnable 任务到异步线程上处理的机制。每一个 EventHandler 和指定的 EventRunner 所创建的新线程绑定,并且该新线程内部有一个事件队列。EventHandler 可以投递指定的 InnerEvent 事件或 Runnable 任务到这个事件队列。EventRunner 从事件队列里循环地取出事件,如果取出的事件是 InnerEvent 事件,将在 EventRunner 所在线程执行 processEvent 回调;如果取出的事件是 Runnable 任务,将在 EventRunner 所在线程执行 Runnable 的 run 回调。一般,EventHandler 有两个主要作用:
在不同线程间分发和处理 InnerEvent 事件或 Runnable 任务。
延迟处理 InnerEvent 事件或 Runnable 任务。
运作机制
EventHandler 的运作机制如下图所示:
使用 EventHandler 实现线程间通信的主要流程:
EventHandler 投递具体的 InnerEvent 事件或者 Runnable 任务到 EventRunner 所创建的线程的事件队列。
EventRunner 循环从事件队列中获取 InnerEvent 事件或者 Runnable 任务。
处理事件或任务:
如果 EventRunner 取出的事件为 InnerEvent 事件,则触发 EventHandler 的回调方法并触发 EventHandler 的处理方法,在新线程上处理该事件。
如果 EventRunner 取出的事件为 Runnable 任务,则 EventRunner 直接在新线程上处理 Runnable 任务。
约束限制
在进行线程间通信的时候,EventHandler 只能和 EventRunner 所创建的线程进行绑定,EventRunner 创建时需要判断是否创建成功,只有确保获取的 EventRunner 实例非空时,才可以使用 EventHandler 绑定 EventRunner。
一个 EventHandler 只能同时与一个 EventRunner 绑定,一个 EventRunner 上可以创建多个 EventHandler。
鸿蒙OS 线程间通信开发指导
EventHandler开发场景
EventHandler 的主要功能是将 InnerEvent 事件或者 Runnable 任务投递到其他的线程进行处理,其使用的场景包括:
开发者需要将 InnerEvent 事件投递到新的线程,按照优先级和延时进行处理。投递时,EventHandler 的优先级可在 IMMEDIATE、HIGH、LOW、IDLE 中选择,并设置合适的 delayTime。
开发者需要将 Runnable 任务投递到新的线程,并按照优先级和延时进行处理。投递时, EventHandler 的优先级可在 IMMEDIATE、HIGH、LOW、IDLE 中选择,并设置合适的 delayTime。
开发者需要在新创建的线程里投递事件到原线程进行处理。
EventRunner工作模式
EventRunner 的工作模式可以分为托管模式和手动模式。两种模式是在调用 EventRunner 的 create() 方法时,通过选择不同的参数来实现的,详见 API 参考。默认为托管模式。
托管模式:不需要开发者调用 run() 和 stop() 方法去启动和停止 EventRunner。当 EventRunner 实例化时,系统调用 run() 来启动 EventRunner;当 EventRunner 不被引用时,系统调用 stop() 来停止 EventRunner。
手动模式:需要开发者自行调用 EventRunner 的 run() 方法和 stop() 方法来确保线程的启动和停止。
接口说明
EventHandler
EventHandler 的属性 Priority (优先级)介绍:
EventRunner 将根据优先级的高低从事件队列中获取事件或者 Runnable 任务进行处理。
EventHandler 的主要接口介绍:
EventRunner
EventRunner的主要接口介绍:
InnerEvent
InnerEvent的属性介绍:
InnerEvent的主要接口介绍:
开发步骤
EventHandler 投递 InnerEvent 事件
EventHandler 投递 InnerEvent 事件,并按照优先级和延时进行处理,开发步骤如下:
创建 EventHandler 的子类,在子类中重写实现方法
processEvent() 来处理事件。private class MyEventHandler extends EventHandler {private MyEventHandler(EventRunner runner) {super(runner);}// 重写实现processEvent方法 public void processEvent(InnerEvent event) {super.processEvent(event);if (event == null) {return;}int eventId = event.eventId;long param = event.param;switch (eventId | param) {case CASE1:// 待执行的操作,由开发者定义break;default:break;}}}
创建 EventRunner,以手动模式为例。
EventRunner runner = EventRunner.create(false);// create()的参数是 true时,则为托管模式// 需要对 EventRunner 的实例进行校验,因为创建 EventRunner 可能失败,如创建线程失败时,创建 EventRunner 失败。if (runner == null) {return;}
创建 EventHandler 子类的实例。
MyEventHandler myHandler = new MyEventHandler(runner);
获取 InnerEvent 事件。
// 获取事件实例,其属性 eventId, param, object 由开发者确定,代码中只是示例。int eventId1 = 0;int eventId2 = 1; long param = 0; Object object = null; InnerEvent event1 = InnerEvent.get(eventId1, param, object);InnerEvent event2 = InnerEvent.get(eventId2, param, object);
投递事件,投递的优先级以 IMMEDIATE 为例,延时选择 0ms和 2ms。
// 优先级 immediate,投递之后立即处理,延时为 0ms,该语句等价于同步投递sendSyncEvent(event1,EventHandler.Priority.immediate);myHandler.sendEvent(event1, 0, EventHandler.Priority.IMMEDIATE);myHandler.sendEvent(event2, 2, EventHandler.Priority.IMMEDIATE); // 延时 2ms 后立即处理
启动和停止 EventRunner,如果为托管模式,则不需要此步骤。
runner.run();//待执行操作runner.stop();// 开发者根据业务需要在适当时机停止 EventRunner
EventHandler 投递 Runnable 任务
EventHandler 投递Runnable 任务,并按照优先级和延时进行处理,开发步骤如下:
创建 EventHandler 的子类,创建 EventRunner,并创建 EventHandler 子类的实例,步骤与[ EventHandler 投递 InnerEvent] 场景的步骤1-3相同。
创建 Runnable 任务。
Runnable task1 = new Runnable() { public void run() {// 待执行的操作,由开发者定义}};Runnable task2 = new Runnable() { public void run() {// 待执行的操作,由开发者定义}};
投递 Runnable 任务,投递的优先级以 IMMEDIATE 为例,延时选择 0ms 和 2ms。
//优先级为 immediate,延时 0ms,该语句等价于同步投递myHandler.postSyncTask(task1,EventHandler.Priority.immediate);myHandler.postTask(task1,0, EventHandler.Priority.IMMEDIATE);myHandler.postTask(task2,2, EventHandler.Priority.IMMEDIATE);// 延时2ms后立即执行
启动和停止 EventRunner,如果是托管模式,则不需要此步骤。
runner.run();//待执行操作runner.stop();// 停止 EventRunner
在新创建的线程里投递事件到原线程
EventHandler 从新创建的线程投递事件到原线程并进行处理,开发步骤如下:
创建 EventHandler 的子类,在子类中重写实现方法 processEvent() 来处理事件。
private class MyEventHandler extends EventHandler {private MyEventHandler(EventRunner runner) {super(runner);}// 重写实现processEvent方法 public void processEvent(InnerEvent event) {super.processEvent(event);if (event == null) {return;}int eventId = event.eventId;long param = event.param;Object object = event.object;switch (eventId | param) {case CASE1:// 待执行的操作,由开发者定义break;case CASE2:// 将原先线程的EventRunner实例投递给新创建的线程if (object instanceof EventRunner) {EventRunner runner2 = (EventRunner)object;}// 将原先线程的EventRunner实例与新创建的线程的EventHandler绑定EventHandler myHandler2 = new EventHandler(runner2) { public void processEvent(InnerEvent event) {//需要在原先线程执行的操作}};int eventId = 1; long param = 0; Object object = null; InnerEvent event2 = InnerEvent.get(eventId, param, object);myHandler2.sendEvent(event2); // 投递事件到原先的线程break;default:break;}}}
创建 EventRunner,以手动模式为例。
EventRunner runner1 = EventRunner.create(false);// create()的参数是true时,则为托管模式。// 需要对 EventRunner 的实例进行校验,不是任何线程都可以通过 create 创建,例如:当线程池已满时,不能再创建线程。if (runner1 == null) {return;}
创建 EventHandler 子类的实例。
MyEventHandler myHandler1 = new MyEventHandler(runner1);
获取 InnerEvent 事件。
// 获取事件实例,其属性 eventId, param, object 由开发者确定,代码中只是示例。int eventId1 = 0;long param = 0; Object object = (Object) EventRunner.current();InnerEvent event1 = InnerEvent.get(eventId1, param, object);
投递事件,在新线程上直接处理。
// 将与当前线程绑定的 EventRunner 投递到与 runner1 创建的新线程中 myHandler.sendEvent(event1);
启动和停止 EventRunner,如果是托管模式,则不需要此步骤。
runner.run();//待执行操作runner.stop();// 停止 EventRunner
**完整代码示例
非托管情况:**
//全局:EventRunner runnerA//线程A:runnerA = EventRunner.create(false);runnerA.run(); // run之后一直循环卡在这里,所以需要新建一个线程run//线程B://1.创建类继承EventHandlerpublic class MyEventHandler extends EventHandler {public static int CODE_DOWNLOAD_FILE1;public static int CODE_DOWNLOAD_FILE2;public static int CODE_DOWNLOAD_FILE3;private MyEventHandler(EventRunner runner) {super(runner);} public void processEvent(InnerEvent event) {super.processEvent(event);if (event == null) {return;}int eventId = event.eventId;if (STOP_EVENT_ID != eventId) {resultEventIdList.add(eventId);}switch (eventId) {case CODE_DOWNLOAD_FILE1: {... // your processbreak;}case CODE_DOWNLOAD_FILE1: {... // your processbreak;}case CODE_DOWNLOAD_FILE1: {... // your processbreak;}default:break;}}}//2.创建 MyEventHandler 实例MyEventHandler handler = new MyEventHandler(runnerA);// 3.向线程 A 发送事件handler.sendEvent(CODE_DOWNLOAD_FILE1);handler.sendEvent(CODE_DOWNLOAD_FILE2);handler.sendEvent(CODE_DOWNLOAD_FILE3);......// 4.runnerA 不再使用后,退出runnerA.stop();
托管情况:
//1.创建 EventRunner A:EventRunner runnerA = EventRunner.create("downloadRunner"); // 内部会新建一个线程//2.创建类继承 EventHandlerpublic class MyEventHandler extends EventHandler {public static int CODE_DOWNLOAD_FILE1;public static int CODE_DOWNLOAD_FILE2;public static int CODE_DOWNLOAD_FILE3;private MyEventHandler(EventRunner runner) {super(runner);} public void processEvent(InnerEvent event) {super.processEvent(event);if (event == null) {return;}int eventId = event.eventId;if (STOP_EVENT_ID != eventId) {resultEventIdList.add(eventId);}switch (eventId) {case CODE_DOWNLOAD_FILE1: {... // your processbreak;}case CODE_DOWNLOAD_FILE1: {... // your processbreak;}case CODE_DOWNLOAD_FILE1: {... // your processbreak;}default:break;}}}//3.创建MyEventHandler实例MyEventHandler handler = new MyEventHandler(runnerA);//4.向线程A发送事件handler.sendEvent(CODE_DOWNLOAD_FILE1);handler.sendEvent(CODE_DOWNLOAD_FILE2);handler.sendEvent(CODE_DOWNLOAD_FILE3);......//5.runnerA没有任何对象引入时,线程会自动回收runnerA = null;
相关文章:

鸿蒙OS 线程间通信
鸿蒙OS 线程间通信概述 在开发过程中,开发者经常需要在当前线程中处理下载任务等较为耗时的操作,但是又不希望当前的线程受到阻塞。此时,就可以使用 EventHandler 机制。EventHandler 是 HarmonyOS 用于处理线程间通信的一种机制,…...
执行 npm报错 Cannot find module ‘../lib/cli.js‘
报错 /usr/local/node/node-v18.20.4-linux-x64/bin/npm node:internal/modules/cjs/loader:1143 throw err; ^ Error: Cannot find module ../lib/cli.js Require stack: - /usr/local/node/node-v18.20.4-linux-x64/bin/npm at Module._resolveFilename (node:inter…...

基于SpringBoot+Vue+MySQL的国产动漫网站
系统展示 用户前台界面 管理员后台界面 系统背景 随着国内动漫产业的蓬勃发展和互联网技术的快速进步,动漫爱好者们对高质量、个性化的国产动漫内容需求日益增长。然而,市场上现有的动漫平台大多以国外动漫为主,对国产动漫的推广和展示存在不…...
AUTOSAR汽车电子嵌入式编程精讲300篇-基于CAN总线的气动控制
目录 前言 知识储备 什么是气动控制: 气动控制基础知识 一、气动元件 二、气路设计 三、气动控制系统 气动控制系统构成图 气动控制系统基本组成功能图 几种常见的气动执行元件实物图 常用气动压力控制阀实物图 常用气动流动控制阀实物图 电磁控制换向发实物图 部…...

Ubuntu 20.04 内核升级后网络丢失问题的解决过程
在 Ubuntu 系统中,内核升级是一个常见的操作,旨在提升系统性能、安全性和兼容性。然而,有时这一操作可能会带来一些意外的副作用,比如导致网络功能的丧失。 本人本来是想更新 Nvidia 显卡的驱动,使用 ubuntu-drivers …...

论文解读《LaMP: When Large Language Models Meet Personalization》
引言:因为导师喊我围绕 “大语言模型的个性化、风格化生成” 展开研究,所以我就找相关论文,最后通过 ACL 官网找到这篇,感觉还不错,就开始解读吧! “说是解读,其实大部分都是翻译哈哈哈&#x…...

Excel VLOOKUP函数怎么用?vlookup函数的使用方法及案例
大家好,这里是效率办公指南! 🔎 在Excel的世界里,VLOOKUP函数无疑是查询和数据分析中的明星。无论是从庞大的数据表中提取特定信息,还是进行数据的快速匹配,VLOOKUP都能大显身手。今天,我们将深…...

专为汽车功能应用打造的 MLX90376GGO、MLX90377GGO、MLX90377GDC-ADB-280 Triaxis®磁位置传感器 IC
一、MLX90376 Triaxis堆叠式高性能位置传感器芯片(模拟/PWM/SENT/SPC) MLX90376GGO-ABA-600 MLX90376GGO-ABA-630 MLX90376GGO-ABA-680 MLX90376是一款磁性绝对位置传感器芯片,适用于要求具备抗杂散磁场干扰性能的360旋转汽车应用。它提供…...

34.贪心算法1
0.贪心算法 1.柠檬水找零(easy) . - 力扣(LeetCode) 题目解析 算法原理 代码 class Solution {public boolean lemonadeChange(int[] bills) {int five 0, ten 0;for (int x : bills) {if (x 5) // 5 元:直接收下…...

DataX实战:从MongoDB到MySQL的数据迁移--修改源码并测试打包
在现代数据驱动的业务环境中,数据迁移和集成是常见的需求。DataX,作为阿里云开源的数据集成工具,提供了强大的数据同步能力,支持多种数据源和目标端。本文将介绍如何使用DataX将数据从MongoDB迁移到MySQL。 环境准备 安装MongoDB…...

Axure设计之表格列冻结(动态面板+中继器)
在Web端产品设计中,复杂的表格展示是常见需求,尤其当表格包含大量列时,如何在有限的屏幕空间内优雅地展示所有信息成为了一个挑战。用户通常需要滚动查看隐藏列,但关键信息列(如ID、操作按钮等)在滚动时保持…...

WPF DataGrid 动态修改某一个单元格的样式
WPF DataGrid 动态修改某一个单元格的样式 <DataGrid Name"main_datagrid_display" Width"1267" Height"193" Grid.Column"1"ItemsSource"{Binding DataGridModels}"><DataGrid.Columns><!--ElementStyle 设…...
如何安装部署kafka
安装和部署Apache Kafka需要以下几个步骤,包括下载 Kafka、配置 ZooKeeper(或者使用 Kafka 自带的 Kafka Raft 模式替代 ZooKeeper),以及启动 Kafka 服务。以下是一个但基于 Linux 的典型安装流程,可以根据需要改装到其…...

Centos7-rpm包管理器方式安装MySQL 5.7.25
前言 本文用于学习通过Mysql压缩包在centos7中安装和配置的过程以及过程中碰到的Bug解决。 Mysql安装包下载和上传 MySQL :: Download MySQL Community Server (Archived Versions)https://downloads.mysql.com/archives/community/访问Mysql官方下载站,选择对应的…...
Project Online 协作版部署方案
目录 前言 第一部分:为什么选择Project Online? 一、核心优势 二、适用场景 第二部分:部署前的准备工作 一、需求分析 二、账户和权限管理 三、培训与支持 第三部分:Project Online 的核心功能 一、项目创建与管理 二、资源管理 三、团队协作 四、风险管理 五…...

小米 13 Ultra机型工程固件 资源预览与刷写说明 步骤解析
小米 13 Ultra机型---机型代码为ishtar 。工程固件可以辅助修复格机或者全檫除分区后的基带修复。可以用于修复TEE损坏。以及一些分区的底层修复。此款固件也可以为更换UFS后的底包。 通过博文了解 1💝💝💝-----此机型工程固件的资源刷写注意事项 2💝💝💝-----此…...
Goweb预防XSS攻击
XSS攻击示例 假设您有一个简单的Web应用程序,其中包含一个用户输入表单,用户可以在其中输入他们的名字,然后这个名字会被显示在页面上。攻击者可以在表单中输入恶意的JavaScript代码,如,如果应用程序没有对这个输入进…...
ICM20948 DMP代码详解(36)
接前一篇文章:ICM20948 DMP代码详解(35) 上一回讲到了icm20948_sensor_setup() ---> inv_icm20948_initialize_auxiliary函数 ---> inv_icm20948_set_slave_compass_id函数,本回开始,就对于inv_icm20948_set_sla…...
【框架】Spring、SpringBoot和SpringCloud区别
Spring Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架) IoC(Inversion of Control,控制反转)是一种设计思想,它主要用于降低软件系统中不同模块之间的耦合度,提高代码的可维护…...
计算机网络各层有哪些协议?
计算机网络的各层协议知识总结 一、物理层 没有涉及到比较重要的协议,但是有一个比较重要的技术----非对称数字用户线(ADSL) 二、数据链路层 1、点对点协议(PPP----point to point protocol,用户计算机与ISP进行通信…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...