在开发过程中,我们经常需要在当前线程中处理下载任务等较为耗时的操作,但是又不希望当前的线程受到阻塞。此时,就可以使用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。
开发部分
1、EventHandler开发场景及EventRunner工作模式
1.1、EventHandler开发场景
- EventHandler投递事件分为两种情况,分别是:使用EventRunner投递事件到新的线程和使用Runnable投递任务到新的线程。两种情况都可以按照优先级和延时处理。投递时,EventHandler的优先级可在IMMEDIATE、HIGH、LOW、IDLE中选择,并设置合适的delayTime。
- 如果需要,我们可以在新创建的线程里投递事件到原线程进行处理。
1.2、EventRunner工作模式
EventRunner的工作模式可以分为托管模式和手动模式。两种模式是在调用EventRunner的create()方法时,通过选择不同的参数来实现的,默认为托管模式。
- 托管模式:不需要我们调用run()和stop()方法去启动和停止EventRunner。当EventRunner实例化时,系统调用run()来启动EventRunner;当EventRunner不被引用时,系统调用stop()来停止EventRunner。
- 手动模式:需要我们自行调用EventRunner的run()方法和stop()方法来确保线程的启动和停止。
2、部分接口说明
2.1、EventHandler
2.1.1、Priority优先级介绍
EventRunner将根据优先级的高低从事件队列中获取事件或者Runnable任务进行处理。Priority是EventHandler中的静态枚举类,分别有四种值(默认优先级为LOW),如下表格:
| 属性 | 描述 |
|---|---|
| Priority.IMMEDIATE | 表示事件被立即投递 |
| Priority.HIGH | 表示事件先于LOW优先级投递 |
| Priority.LOW | 表示事件优于IDLE优先级投递,事件的默认优先级是LOW |
| Priority.IDLE | 表示在没有其他事件的情况下,才投递该事件 |
2.1.2、EventHandler部分接口介绍
| 接口名 | 描述 |
|---|---|
| EventHandler(EventRunner runner) | 利用已有的EventRunner来创建EventHandler |
| current() | 在processEvent回调中,获取当前的EventHandler |
| processEvent(InnerEvent event) | 回调处理事件,在里边处理我们需要的逻辑 |
| sendEvent(InnerEvent event, long delayTime, EventHandler.Priority priority) | 发送一个指定优先级的延时事件到事件队列 |
| sendSyncEvent(InnerEvent event, EventHandler.Priority priority) | 发送一个指定优先级的同步事件到事件队列,延时为0ms,优先级不可以是IDLE |
| postSyncTask(Runnable task, EventHandler.Priority priority) | 发送一个指定优先级的Runnable同步任务到事件队列,延时为0ms |
| postTask(Runnable task, long delayTime, EventHandler.Priority priority) | 发送一个指定优先级的Runnable延时任务到事件队列 |
| sendTimingEvent(InnerEvent event, long taskTime, EventHandler.Priority priority) | 发送一个带优先级的定时事件到队列,在taskTime时间执行,如果taskTime小于当前时间,立即执行 |
| postTimingTask(Runnable task, long taskTime, EventHandler.Priority priority) | 发送一个带优先级的定时Runnable任务到队列,在taskTime时间执行,如果taskTime小于当前时间,立即执行 |
| removeEvent(int eventId, long param, Object object) | 删除指定id、param和object的事件 |
| removeAllEvent() | 删除该EventHandler的所有事件 |
| getEventName(InnerEvent event) | 获取事件的名字 |
| getEventRunner() | 获取该EventHandler绑定的EventRunner |
| isIdle() | 判断队列是否为空 |
| hasInnerEvent(Runnable runnable) | 根据指定的runnable参数,检查是否有还未被处理的任务。可以根据不同的入参进行检查 |
2.2、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为手动模式时,调用该方法停止新的线程 |
2.3、InnerEvent
2.3.1、InnerEvent的属性介绍
| 属性 | 描述 |
|---|---|
| eventId | 事件的ID, 由开发者定义用来辨别事件 |
| object | 事件携带的Object信息 |
| param | 事件携带的long型数据 |
2.3.2、InnerEvent的主要接口介绍
| 接口名 | 描述 |
|---|---|
| drop() | 释放一个事件实例 |
| get() | 获得一个事件实例 |
| get(int eventId, long param, Object object) | 获得一个指定的eventId,param和object的事件实例 |
| PacMap getPacMap() | 获取PacMap,如果没有,会新建一个 |
| Runnable getTask() | 获取Runnable任务 |
| PacMap peekPacMap() | 获取PacMap |
| void setPacMap(PacMap pacMap) | 设置PacMap |
3、实战
在实战中,我将使用手动模式来编写案例。托管模式会在代码创建EventRunner实例后,就调用run()进行开启线程,我们只需要在不使用或退出的时候将EventRunner实例赋值为null,系统就会调用stop()来停止EventRunner。
3.1、EventHandler投递InnerEvent事件
案例:EventHandler投递InnerEvent事件,并按照优先级和延时进行处理,开发步骤如下:
1)创建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方法
@Override
public 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;
}
}
}
2)创建EventRunner。
// create()的参数是true时,则为托管模式
EventRunner runner = EventRunner.create(false);
3)创建EventHandler子类的实例。
MyEventHandler myHandler = new MyEventHandler(runner);
4)获取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);
5)投递事件,投递的优先级以IMMEDIATE为例,延时选择0ms和2ms。
// 优先级IMMEDIATE,投递之后立即处理,延时为0ms,该语句等价于同步投递sendSyncEvent(event1,EventHandler.Priority.IMMEDIATE);
myHandler.sendEvent(normalInnerEvent, 0, EventHandler.Priority.IMMEDIATE);
myHandler.sendEvent(delayInnerEvent, 2, EventHandler.Priority.IMMEDIATE); // 延时2ms后立即处理
6)启动和停止EventRunner,如果为托管模式,则不需要此步骤。
runner.run();
// 待执行操作
runner.stop();// 开发者根据业务需要在适当时机停止EventRunner
3.2、EventHandler投递Runnable任务
案例:EventHandler投递Runnable任务,并按照优先级和延时进行处理,开发步骤如下:
1)创建EventHandler的子类,创建EventRunner,并创建EventHandler子类的实例,步骤与EventHandler投递InnerEvent场景的步骤1-3相同。 2)创建Runnable任务。
Runnable normalTask = new Runnable() {
@Override
public void run() {
// 待执行的操作,由开发者定义
}
};
Runnable delayTask = new Runnable() {
@Override
public void run() {
// 待执行的操作,由开发者定义
}
};
3)投递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后立即执行
4)启动和停止EventRunner,如果是托管模式,则不需要此步骤。
runner.run();
// 待执行操作
runner.stop();// 停止EventRunner
3.3、在新创建的线程里投递事件到原线程(*)
案例:EventHandler从新创建的线程投递事件到原线程并进行处理,开发步骤如下:
1)创建EventHandler的子类,在子类中重写实现方法processEvent()来处理事件。
private static final int EVENT_MESSAGE_CROSS_THREAD = 1;
private class MyEventHandler extends EventHandler {
private MyEventHandler(EventRunner runner) {
super(runner);
}
// 重写实现processEvent方法
@Override
public 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) {
@Override
public 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;
}
}
}
2)创建EventRunner,以手动模式为例。
EventRunner runner = EventRunner.create(false);// create()的参数是true时,则为托管模式。
3)创建EventHandler子类的实例。
MyEventHandler myHandler = new MyEventHandler(runner);
4)投递事件,在新线程上直接处理。
// 将与当前线程绑定的EventRunner投递到与runner创建的新线程中
myHandler.sendEvent(event);
5)启动和停止EventRunner,如果是托管模式,则不需要此步骤。
runner.run();
// 待执行操作
runner.stop();// 停止EventRunner
小结
在开发部分,我们先了解了EventHandler开发场景、EventRunner工作模式,学习了EventHandler、EventRunner、InnerEvent相关接口以及EventHandler投递事件或任务优先级的设置。最后我们进行了实战,实战的内容也比较简单。总结来说就是,首先先定义一个EventHandler实例并重写processEvent()方法,再创建EventRunner实例对象,利用创建好的EventRunner实例对象来创建EventHandler对象,我们使用InnerEvent.get()来创建/获取一个事件实例,new Runnable()来创建一个任务,然后调用EventHandler相应的方法来进行事件或任务的投递,最后我们在processEvent()方法中处理我们的逻辑。
思考总结
- 为什么要使用EventHandler机制?
- 理解EventRunner、InnerEvent、EventHandler。
- EventHandler的两个主要作用是什么?
- EventHandler的开发场景以及EventRunner的工作模式?
- 了解EventHandler、EventRunner以及InnerEvent的部分主要接口。
- 如何实现在新创建的线程里投递事件到原线程?