HarmonyOS之深入解析线程间的通信
一、概述
① 基本概念
- 在开发过程中,开经常需要在当前线程中处理下载任务等较为耗时的操作,但是又不希望当前的线程受到阻塞。此时,就可以使用 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。
二、应用场景
① EventHandler 开发场景
- EventHandler 的主要功能是将 InnerEvent 事件或者 Runnable 任务投递到其他的线程进行处理,其使用的场景包括:
- 开发者需要将 InnerEvent 事件投递到新的线程,按照优先级和延时进行处理。投递时,EventHandler 的优先级可在 IMMEDIATE、HIGH、LOW、IDLE 中选择,并设置合适的 delayTime。
- 开发者需要将 Runnable 任务投递到新的线程,并按照优先级和延时进行处理。投递时,EventHandler 的优先级可在 IMMEDIATE、HIGH、LOW、IDLE 中选择,并设置合适的 delayTime。
- 需要在新创建的线程里投递事件到原线程进行处理。
② EventRunner 工作模式
- EventRunner 的工作模式可以分为托管模式和手动模式。两种模式是在调用 EventRunner 的 create() 方法时,通过选择不同的参数来实现的,默认为托管模式。
- 托管模式:不需要调用 run() 和 stop() 方法去启动和停止 EventRunner。当 EventRunner 实例化时,系统调用 run() 来启动 EventRunner;当 EventRunner 不被引用时,系统调用 stop() 来停止 EventRunner。
- 手动模式:需要自行调用 EventRunner 的 run() 方法和 stop() 方法来确保线程的启动和停止。
三、API 说明
① EventHandler
- EventHandler 的属性 Priority(优先级):EventRunner 将根据优先级的高低从事件队列中获取事件或者 Runnable 任务进行处理。
- EventHandler 的属性:
属性 | 描述 |
---|---|
Priority.IMMEDIATE | 表示事件被立即投递 |
Priority.HIGH | 表示事件先于LOW优先级投递 |
Priority.LOW | 表示事件优于IDLE优先级投递,事件的默认优先级是LOW |
Priority.IDLE | 表示在没有其他事件的情况下,才投递该事件 |
- EventHandler 的主要接口:
接口名 | 描述 |
---|---|
EventHandler(EventRunner runner) | 利用已有的EventRunner来创建EventHandler |
current() | 在processEvent回调中,获取当前的EventHandler |
processEvent(InnerEvent event) | 回调处理事件,由开发者实现 |
sendEvent(InnerEvent event) | 发送一个事件到事件队列,延时为0ms, 优先级为LOW |
sendEvent(InnerEvent event, long delayTime) | 发送一个延时事件到事件队列,优先级为LOW |
sendEvent(InnerEvent event, long delayTime, EventHandler.Priority priority) | 发送一个指定优先级的延时事件到事件队列 |
sendEvent(InnerEvent event, EventHandler.Priority priority) | 发送一个指定优先级的事件到事件队列,延时为0ms |
sendSyncEvent(InnerEvent event) | 发送一个同步事件到事件队列,延时为0ms,优先级为LOW |
sendSyncEvent(InnerEvent event, EventHandler.Priority priority) | 发送一个指定优先级的同步事件到事件队列,延时为0ms,优先级不可以是IDLE |
postSyncTask(Runnable task) | 发送一个Runnable同步任务到事件队列,延时为0ms, 优先级为LOW |
postSyncTask(Runnable task, EventHandler.Priority priority) | 发送一个指定优先级的Runnable同步任务到事件队列,延时为0ms |
postTask(Runnable task) | 发送一个Runnable任务到事件队列,延时为0ms,优先级为LOW |
postTask(Runnable task, long delayTime) | 发送一个Runnable延时任务到事件队列,优先级为LOW |
postTask(Runnable task, long delayTime, EventHandler.Priority priority) | 发送一个指定优先级的Runnable延时任务到事件队列 |
postTask(Runnable task, EventHandler.Priority priority) | 发送一个指定优先级的Runnable任务到事件队列,延时为0ms |
sendTimingEvent(InnerEvent event, long taskTime) | 发送一个定时事件到队列,在taskTime时间执行,如果taskTime小于当前时间,立即执行,优先级为LOW |
sendTimingEvent(InnerEvent event, long taskTime, EventHandler.Priority priority) | 发送一个带优先级的事件到队列,在taskTime时间执行,如果taskTime小于当前时间,立即执行 |
postTimingTask(Runnable task, long taskTime) | 发送一个Runnable任务到队列,在taskTime时间执行,如果taskTime小于当前时间,立即执行,优先级为LOW |
postTimingTask(Runnable task, long taskTime, EventHandler.Priority priority) | 发送一个带优先级的Runnable任务到队列,在taskTime时间执行,如果taskTime小于当前时间,立即执行 |
removeEvent(int eventId) | 删除指定id的事件 |
removeEvent(int eventId, long param) | 删除指定id和param的事件 |
removeEvent(int eventId, long param, Object object) | 删除指定id、param和object的事件 |
removeAllEvent() | 删除该EventHandler的所有事件 |
getEventName(InnerEvent event) | 获取事件的名字 |
getEventRunner() | 获取该EventHandler绑定的EventRunner |
isIdle() | 判断队列是否为空 |
hasInnerEvent(Runnable runnable) | 根据指定的runnable参数,检查是否有还未被处理的任务。可以根据不同的入参进行检查,详见EventHandler |
② EventRunner
- EventRunner 主要接口:
接口名 | 描述 |
---|---|
create() | 创建一个拥有新线程的EventRunner |
create(boolean inNewThread) | 创建一个拥有新线程的EventRunner,inNewThread为true时,EventRunner为托管模式,系统将自动管理该EventRunner;inNewThread为false时,EventRunner为手动模式 |
create(String newThreadName) | 创建一个拥有新线程的EventRunner, 新线程的名字是 newThreadName |
current() | 获取当前线程的EventRunner |
run() | EventRunner为手动模式时,调用该方法启动新的线程 |
stop() | EventRunner为手动模式时,调用该方法停止新的线程 |
③ InnerEvent
- InnerEvent 的属性:
属性 | 描述 |
---|---|
eventId | 事件的ID, 由开发者定义用来辨别事件 |
object | 事件携带的Object信息 |
param | 事件携带的long型数据 |
- InnerEvent 的接口:
接口名 | 描述 |
---|---|
drop() | 释放一个事件实例 |
get() | 获得一个事件实例 |
get(int eventId) | 获得一个指定的eventId的事件实例 |
get(int eventId, long param) | 获得一个指定的eventId和param的事件实例 |
get(int eventId, long param, Object object) | 获得一个指定的eventId,param和object的事件实例 |
get(int eventId, Object object) | 获得一个指定的eventId和object的事件实例 |
PacMap getPacMap() | 获取PacMap,如果没有,会新建一个 |
Runnable getTask() | 获取Runnable任务 |
PacMap peekPacMap() | 获取PacMap |
void setPacMap(PacMap pacMap) | 设置PacMap |
四、线程的通信使用
① EventHandler 投递 InnerEvent 事件
- EventHandler 投递 InnerEvent 事件,并按照优先级和延时进行处理。
- 创建 EventHandler 的子类,在子类中重写实现方法 processEvent() 来处理事件:
private static final int EVENT_MESSAGE_NORMAL = 1;private static final int EVENT_MESSAGE_DELAY = 2;private class MyEventHandler extends EventHandler {private MyEventHandler(EventRunner runner) {super(runner);}// 重写实现processEvent方法@Overridepublic void processEvent(InnerEvent event) {super.processEvent(event);if (event == null) {return;}int eventId = event.eventId;switch (eventId) {case EVENT_MESSAGE_NORMAL:// 待执行的操作,由开发者定义break;case EVENT_MESSAGE_DELAY:// 待执行的操作,由开发者定义break;default:break;}}}
- 创建 EventRunner,以手动模式为例:
EventRunner runner = EventRunner.create(false);// create()的参数是true时,则为托管模式
- 创建 EventHandler 子类的实例:
MyEventHandler myHandler = new MyEventHandler(runner);
- 获取 InnerEvent 事件:
// 获取事件实例,其属性eventId, param, object由开发者确定,代码中只是示例long param = 0L; Object object = null; InnerEvent normalInnerEvent = InnerEvent.get(EVENT_MESSAGE_NORMAL, param, object);InnerEvent delayInnerEvent = InnerEvent.get(EVENT_MESSAGE_DELAY, param, object);
- 投递事件,投递的优先级以 IMMEDIATE 为例,延时选择 0ms 和 2m:
// 优先级IMMEDIATE,投递之后立即处理,延时为0ms,该语句等价于同步投递sendSyncEvent(event1,EventHandler.Priority.IMMEDIATE);myHandler.sendEvent(normalInnerEvent, 0, EventHandler.Priority.IMMEDIATE);myHandler.sendEvent(delayInnerEvent, 2, EventHandler.Priority.IMMEDIATE); // 延时2ms后立即处理
- 启动和停止 EventRunner,如果为托管模式,则不需要此步骤:
runner.run();// 待执行操作...runner.stop();// 开发者根据业务需要在适当时机停止EventRunner
② EventHandler 投递 Runnable 任务
- EventHandler 投递 Runnable 任务,并按照优先级和延时进行处理。
- 创建 EventHandler 的子类,创建 EventRunner,并创建 EventHandler 子类的实例,步骤与 EventHandler 投递 InnerEvent 场景的前三个步骤相同。
- 创建 Runnable 任务。
Runnable normalTask = new Runnable() {@Overridepublic void run() {// 待执行的操作,由开发者定义}};Runnable delayTask = new Runnable() {@Overridepublic void run() {// 待执行的操作,由开发者定义}};
- 投递 Runnable 任务,投递的优先级以 IMMEDIATE 为例,延时选择 0ms 和 2ms。
// 优先级为immediate,延时0ms,该语句等价于同步投递myHandler.postSyncTask(task1,EventHandler.Priority.IMMEDIATE);myHandler.postTask(normalTask, 0, EventHandler.Priority.IMMEDIATE);myHandler.postTask(delayTask, 2, EventHandler.Priority.IMMEDIATE);// 延时2ms后立即执行
- 启动和停止 EventRunner,如果是托管模式,则不需要此步骤:
runner.run();// 待执行操作...runner.stop();// 停止EventRunner
③ 在新创建的线程里投递事件到原线程
- EventHandler 从新创建的线程投递事件到原线程并进行处理。
- 创建 EventHandler 的子类,在子类中重写实现方法 processEvent() 来处理事件。
private static final int EVENT_MESSAGE_CROSS_THREAD = 1;private class MyEventHandler extends EventHandler {private MyEventHandler(EventRunner runner) {super(runner);}// 重写实现processEvent方法@Overridepublic void processEvent(InnerEvent event) {super.processEvent(event);if (event == null) {return;}int eventId = event.eventId;switch (eventId) {case EVENT_MESSAGE_CROSS_THREAD:Object object = event.object;if (object instanceof EventRunner) {// 将原先线程的EventRunner实例投递给新创建的线程EventRunner runner2 = (EventRunner) object;// 将原先线程的EventRunner实例与新创建的线程的EventHandler绑定EventHandler myHandler2 = new EventHandler(runner2) {@Overridepublic void processEvent(InnerEvent event) {// 需要在原先线程执行的操作}};int eventId2 = 1; long param2 = 0L; Object object2 = null; InnerEvent event2 = InnerEvent.get(eventId2, param2, object2);myHandler2.sendEvent(event2); // 投递事件到原先的线程}break;default:break;}}}
- 创建 EventRunner,以手动模式为例:
EventRunner runner = EventRunner.create(false);// create()的参数是true时,则为托管模式
- 创建 EventHandler 子类的实例:
MyEventHandler myHandler = new MyEventHandler(runner);
- 获取 InnerEvent 事件:
// 获取事件实例,其属性eventId, param, object由开发者确定,代码中只是示例long param = 0L; InnerEvent event = InnerEvent.get(EVENT_MESSAGE_CROSS_THREAD, param, EventRunner.current());
- 投递事件,在新线程上直接处理:
// 将与当前线程绑定的EventRunner投递到与runner创建的新线程中myHandler.sendEvent(event);
- 启动和停止 EventRunner,如果是托管模式,则不需要此步骤:
runner.run();// 待执行操作...runner.stop();// 停止EventRunner
五、完整示例
- 非托管情况:
// 全局:public static final int CODE_DOWNLOAD_FILE1 = 1;public static final int CODE_DOWNLOAD_FILE2 = 2;public static final int CODE_DOWNLOAD_FILE3 = 3;// 线程A:EventRunner runnerA = EventRunner.create(false);runnerA.run(); // run之后一直循环卡在这里,所以需要新建一个线程run// 线程B:// 1.创建类继承EventHandlerpublic class MyEventHandler extends EventHandler {private MyEventHandler(EventRunner runner) {super(runner);}@Overridepublic void processEvent(InnerEvent event) {super.processEvent(event);if (event == null) {return;}int eventId = event.eventId;switch (eventId) {case CODE_DOWNLOAD_FILE1: {// 待执行的操作,由开发者定义break;}case CODE_DOWNLOAD_FILE2: {// 待执行的操作,由开发者定义break;}case CODE_DOWNLOAD_FILE3: {// 待执行的操作,由开发者定义break;}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();
- 托管情况:
// 全局:public static final int CODE_DOWNLOAD_FILE1 = 1;public static final int CODE_DOWNLOAD_FILE2 = 2;public static final int CODE_DOWNLOAD_FILE3 = 3;// 1.创建EventRunner A:EventRunner runnerA = EventRunner.create("downloadRunner"); // 内部会新建一个线程// 2.创建类继承EventHandlerpublic class MyEventHandler extends EventHandler {private MyEventHandler(EventRunner runner) {super(runner);}@Overridepublic void processEvent(InnerEvent event) {super.processEvent(event);if (event == null) {return;}int eventId = event.eventId;switch (eventId) {case CODE_DOWNLOAD_FILE1: {// 待执行的操作,由开发者定义break;}case CODE_DOWNLOAD_FILE2: {// 待执行的操作,由开发者定义break;}case CODE_DOWNLOAD_FILE3: {// 待执行的操作,由开发者定义break;}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;
HarmonyOS之深入解析线程间的通信相关推荐
- Java 多线程(七) 线程间的通信
Java 多线程(七) 线程间的通信--wait及notify方法 线程间的相互作用 线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务. Object类中相关的方法有两个notify方法 ...
- Android开发之Service通过Messenger实现线程间的通信
Messenger信使其实在真正使用中用到的并不多,但是面试的时候考官还是十分想要考察一下你的基本功的.那我们现在先来看一下Messenger的使用场景.如果你需要你的Service与远程线程通信,那 ...
- 线程间的通信 共享数据安全问题
1 //线程间的通信:线程的任务不同,但是线程操作的数据相同. 2 3 //描述数据 4 class Resource 5 { 6 public String name; 7 public Strin ...
- 高并发编程-使用wait和notifyAll进行线程间的通信3_多线程下的生产者消费者模型和notifyAll
文章目录 概述 解决办法 概述 高并发编程-线程通信_使用wait和notify进行线程间的通信2_多生产者多消费者导致程序假死原因分析 中分析了假死的原因,这里我们来看下改如何解决在多线程下出现的这 ...
- 高并发编程-线程通信_使用wait和notify进行线程间的通信2_多生产者多消费者导致程序假死原因分析
文章目录 概述 jstack或者可视化工具检测是否死锁(没有) 原因分析 概述 高并发编程-线程通信_使用wait和notify进行线程间的通信 - 遗留问题 我们看到了 应用卡住了 .... 怀疑是 ...
- 高并发编程-线程通信_使用wait和notify进行线程间的通信
文章目录 概述 场景 引子 synchronized wait/notify机制 synchronized wait/notify 改造 问题 概述 Java中线程通信协作的最常见的两种方式: syn ...
- educoder 使用线程锁(lock)实现线程同步_线程间的通信(一)
这篇文章主要从4个角度来讲多线程间的通信: 使用wait/notify实现线程间的通信 生产者/消费者模式的实现 方法join的使用 ThreadLocal类的使用 等待/通知机制的实现: (1)wa ...
- java 线程间通信 handler_Handler不同线程间的通信
转http://www.iteye.com/problems/69457 Activity启动后点击一个界面按钮后会开启一个服务(暂定为padService),在padService中会启动一个线程( ...
- Java中condition的用法_java5 Condition用法--实现线程间的通信
Condition的功能类似在传统线程技术中的Object.wait()和Object.natify()的功能,传统线程技术实现的互斥只能一个线程单独干,不能说这个线程干完了通知另一个线程来干,Con ...
最新文章
- python实现洗牌算法_洗牌算法及 random 中 shuffle 方法和 sample 方法浅析
- 特约专栏丨王耀南院士:人工智能赋能无人系统
- 设计模式-生产者消费者模式
- 通过动效学习UI设计
- 关于AutoResetEvent 和ManualResetEvent
- animate 实现滑动切换效果
- 322. 零钱兑换 golang 动态规划
- php 结尾,PHP“意外结束”
- TCP/IP协议栈:TCP超时重传机制
- infomixdb mysql_不同数据库oracle mysql SQL Server DB2 infomix sybase分页查询语句
- java json插件安装_IDEAL葵花宝典:java代码开发规范插件:GsonFormat插件将JSONObject格式的String 解析成实体...
- php获取li或者a标签中间的文字
- Android高级开发-布局渲染流程与优化
- PHP用301重定向根域名到www域名
- CentOS 7安装Mysql并设置开机自启动
- centos7使用kubeadm部署k8s集群(使用containerd做运行时)
- 概率分布之二项分布与多项分布
- Mac OSX 安装qemu
- 计算机网络分层汇聚层,大型局域网通常划分为核心层、汇聚层和接入层,以下关于各个网络层次的描述中,不 - 问答库...
- html5页面拨打电话,5.添加页面/设置点击拨打电话