HarmonyOS之深入解析线程间的通信

Posted Forever_wj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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方法
	    @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;
	        }
	    }
	}
  • 创建 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() {
	    @Override
	    public void run() {
	        // 待执行的操作,由开发者定义
	    }
	};
	Runnable delayTask = new Runnable() {
	    @Override
	    public 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方法
	    @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;
	        }
	    }
	}
  • 创建 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.创建类继承EventHandler
	public class MyEventHandler extends EventHandler {
	    private MyEventHandler(EventRunner runner) {
	        super(runner);
	    }
	
	    @Override
	    public 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.创建类继承EventHandler
	public class MyEventHandler extends EventHandler {
	    private MyEventHandler(EventRunner runner) {
	        super(runner);
	    }
	
	    @Override
	    public 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之深入解析线程间的通信的主要内容,如果未能解决你的问题,请参考以下文章

HarmonyOS之深入解析NFC的功能和使用

HarmonyOS之深入解析蓝牙Bluetooth的功能和使用

HarmonyOS之深入解析通知的使用

HarmonyOS之深入解析媒体会话的管理

HarmonyOS之深入解析编译构建的配置和代码混淆

HarmonyOS之深入解析设备标识符的功能和使用