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

鸿蒙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,0EventHandler.Priority.IMMEDIATE);myHandler.postTask(task2,2EventHandler.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 用于处理线程间通信的一种机制&#xff0c…...

执行 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需要以下几个步骤&#xff0c;包括下载 Kafka、配置 ZooKeeper&#xff08;或者使用 Kafka 自带的 Kafka Raft 模式替代 ZooKeeper&#xff09;&#xff0c;以及启动 Kafka 服务。以下是一个但基于 Linux 的典型安装流程&#xff0c;可以根据需要改装到其…...

Centos7-rpm包管理器方式安装MySQL 5.7.25

前言 本文用于学习通过Mysql压缩包在centos7中安装和配置的过程以及过程中碰到的Bug解决。 Mysql安装包下载和上传 MySQL :: Download MySQL Community Server (Archived Versions)https://downloads.mysql.com/archives/community/访问Mysql官方下载站&#xff0c;选择对应的…...

Project Online 协作版部署方案

目录 前言 第一部分:为什么选择Project Online? 一、核心优势 二、适用场景 第二部分:部署前的准备工作 一、需求分析 二、账户和权限管理 三、培训与支持 第三部分:Project Online 的核心功能 一、项目创建与管理 二、资源管理 三、团队协作 四、风险管理 五…...

小米 13 Ultra机型工程固件 资源预览与刷写说明 步骤解析

小米 13 Ultra机型---机型代码为ishtar 。工程固件可以辅助修复格机或者全檫除分区后的基带修复。可以用于修复TEE损坏。以及一些分区的底层修复。此款固件也可以为更换UFS后的底包。 通过博文了解 1💝💝💝-----此机型工程固件的资源刷写注意事项 2💝💝💝-----此…...

Goweb预防XSS攻击

XSS攻击示例 假设您有一个简单的Web应用程序&#xff0c;其中包含一个用户输入表单&#xff0c;用户可以在其中输入他们的名字&#xff0c;然后这个名字会被显示在页面上。攻击者可以在表单中输入恶意的JavaScript代码&#xff0c;如&#xff0c;如果应用程序没有对这个输入进…...

ICM20948 DMP代码详解(36)

接前一篇文章&#xff1a;ICM20948 DMP代码详解&#xff08;35&#xff09; 上一回讲到了icm20948_sensor_setup() ---> inv_icm20948_initialize_auxiliary函数 ---> inv_icm20948_set_slave_compass_id函数&#xff0c;本回开始&#xff0c;就对于inv_icm20948_set_sla…...

【框架】Spring、SpringBoot和SpringCloud区别

Spring Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器&#xff08;框架&#xff09; IoC&#xff08;Inversion of Control&#xff0c;控制反转&#xff09;是一种设计思想&#xff0c;它主要用于降低软件系统中不同模块之间的耦合度&#xff0c;提高代码的可维护…...

计算机网络各层有哪些协议?

计算机网络的各层协议知识总结 一、物理层 没有涉及到比较重要的协议&#xff0c;但是有一个比较重要的技术----非对称数字用户线&#xff08;ADSL&#xff09; 二、数据链路层 1、点对点协议&#xff08;PPP----point to point protocol&#xff0c;用户计算机与ISP进行通信…...

基于遗忘因子递推最小二乘法的电池模型参数在线辨识与优化

1. 电池模型参数辨识为什么需要FFRLS算法 我第一次接触电池参数辨识是在开发一款智能硬件时&#xff0c;当时发现传统最小二乘法有个致命问题——它会把所有历史数据同等对待。这就像用算盘计算平均数时&#xff0c;不管数据是昨天还是去年的&#xff0c;都按相同权重处理。但在…...

GG3M 元模型完整详解:从东方哲学数学化到文明级智慧操作系统

GG3M 元模型完整详解&#xff1a;从东方哲学数学化到文明级智慧操作系统摘要&#xff1a; GG3M 是全球首个以贾子理论&#xff08;Kucius Theory&#xff09;为核心、定位文明级智慧操作系统的 AGI 项目。其元模型&#xff08;Meta-Model&#xff09;以 3M 三层架构&#xff08…...

霍尔电流传感器原理与应用全解析

霍尔电流传感器测量原理深度解析 1. 霍尔效应电流测量基础 1.1 霍尔效应原理 霍尔效应是电流测量的基础物理现象&#xff0c;当导体或半导体薄片置于磁场中&#xff0c;并在其两端施加控制电流时&#xff0c;垂直于电流和磁场方向的两侧会产生电势差&#xff0c;这种现象称为…...

UniApp地图组件实战:5分钟搞定腾讯位置服务+自定义气泡弹窗(附避坑指南)

UniApp腾讯地图组件深度实战&#xff1a;从Key申请到自定义弹窗全流程解析 1. 腾讯位置服务Key申请与配置 在manifest.json中配置腾讯地图Key是第一步&#xff0c;但90%的开发者会忽略安全配置细节。正确的申请流程应该是&#xff1a; 访问腾讯位置服务官网&#xff0c;进入控制…...

MAG3110磁力计驱动开发与地磁导航嵌入式实践

1. MAG3110三轴数字磁力计技术解析与嵌入式驱动开发实践MAG3110是由NXP&#xff08;恩智浦&#xff09;半导体推出的高精度、低功耗三轴数字磁力计&#xff0c;专为电子罗盘&#xff08;eCompass&#xff09;、姿态检测、位置感知及工业磁场监测等场景设计。该器件采用IC接口通…...

Android 12 蓝牙权限适配指南:从经典到低功耗的全面解析

1. Android 12蓝牙权限变革全景解读 去年给医疗设备厂商做BLE固件升级功能时&#xff0c;突然发现测试机上的蓝牙扫描失灵了。排查半天才发现是targetSdkVersion升级到31后&#xff0c;沿用老权限方案导致的兼容性问题。这次踩坑经历让我深刻意识到&#xff0c;Android 12的蓝牙…...

【LaTeX】学术论文高效排版:从零搭建初稿模板

1. 为什么你需要LaTeX论文模板&#xff1f; 第一次写学术论文时&#xff0c;我像大多数人一样打开了Word。结果光是调整格式就花了三天——页码突然跑到封面中间、参考文献编号莫名其妙重置、公式和图片永远对不齐。直到导师扔给我一个.tex文件说"用这个"&#xff0c…...

鸿蒙ArkTS项目避坑指南:从零搭建外卖应用时,我踩过的那些‘坑’

鸿蒙ArkTS实战避坑手册&#xff1a;外卖应用开发中的12个致命陷阱 第一次在DevEco Studio里看到ArkTS的语法高亮时&#xff0c;我以为这不过是又一个前端框架的变种——直到我的外卖应用项目在模拟器上连续崩溃了七次。作为从Android原生开发转向鸿蒙的"老手"&#x…...

FireRed-OCR StudioGPU适配方案:多卡并行解析长文档的配置详解

FireRed-OCR StudioGPU适配方案&#xff1a;多卡并行解析长文档的配置详解 1. 工业级文档解析工具概述 FireRed-OCR Studio是一款基于Qwen3-VL模型开发的下一代文档解析工具&#xff0c;专为处理复杂文档场景设计。它不仅能够精准识别文字内容&#xff0c;更能完整还原文档中…...

Cobalt视频下载工具:创作者必备的素材管理与备份完整指南

Cobalt视频下载工具&#xff1a;创作者必备的素材管理与备份完整指南 【免费下载链接】cobalt save what you love 项目地址: https://gitcode.com/GitHub_Trending/cob/cobalt 在数字内容创作的世界里&#xff0c;素材管理是每个创作者都面临的挑战。Cobalt视频下载工具…...