第33333333章
HarmonyOS事件处理
本章要点
.HarmonyOS基于监听的事件处理
课程思政
3
.HarmonyOS线程管理
.HarmonyOS线程间通信
本章知识结构图(见图3-1)
图3-
1
本章知识结构图
第3章HarmonyOS
事件处理
图3-1(续)
本章示例(见图3-2)
图3-
2
本章示例图
通过前面两章学习了HarmonyOS 所提供的一些功能强大的界面控件,这些控件主
要用来进行界面的设计与数据的显示,只能实现静态UI,但是如果用户想与之进行动态
交互,获取用户的意图信息,实现更加具体的功能,则还需要相应事件处理的辅助。当用
户在程序界面上执行各种操作时,如单击一个按钮,此时一个体验良好的应用程序通常会
做出相应的响应,这种响应就是通过事件处理来完成的。在前面章节的学习中,已经使用
到了HarmonyOS 中的一些事件处理,例如单击按钮实现数字加一。
在HarmonyOS 中,主线程又可以叫作UI 线程,用户界面属于主线程,默认情况下,
所有的操作都在主线程上执行。如果需要执行比较耗时的任务,可创建其他线程(或子线
程)来处理,否则主线程会因为耗时操作被阻塞,从而出现异常,所以需要不同的任务分发
器来应对不同的操作情况。此外,还需要思考如何才能动态地显示用户界面。本章将介
绍通过EventHandler消息传递来动态更新界面。
如果在事件处理中需要做一些比较耗时的操作,直接放在主线程中将会阻塞程序的
85
鸿蒙应用开发教程
运行,给用户不好的体验,甚至程序会没有响应或强制退出。本章将学习通过同步派发任
务以外的方式来处理耗时的操作。
学完本章之后,再结合前面所学知识,读者将可以开发出界面友好、人机交互良好的
HarmonyOS 应用。
3.amonyOS
基于监听的事件处理
1
Hr
不管是什么手机应用,都离不开与用户的交互,只有通过用户的操作,才能知道用户
的需求,从而实现具体的业务功能,因此,应用中经常需要处理的就是用户的操作,也就是
需要为用户的操作提供响应,这种为用户操作提供响应的机制就是事件处理。
HarmonyOS 的基于监听的事件处理模型,与Java的AWT 、Swing的处理方式几乎
完全一样,只是相应的事件监听器和事件处理方法名有所不同。在基于监听的事件处理
模型中,主要涉及以下3类对象。
.Eventsource(事件源): 即事件发生的源头,通常为某一控件,如图片、列表等。
.Event(事件):用户具体某一操作的详细描述。事件封装了该操作的相关信息,
如果程序需要获得事件源上所发生事件的相关信息,一般通过Event对象来取
得,例如按键事件按下的是哪个键、触摸事件发生的位置等。
.Eventlistener(事件监听器):负责监听用户在事件源上的操作,并对用户的各种
操作做出相应的响应。事件监听器中可包含多个事件处理器,一个事件处理器实
际上就是一个事件处理方法。
在基于监听的事件处理中,这3类对象又是如何协作的呢? 实际上,基于监听的事件
处理是一种委托式事件处理。事件源即普通控件将整个事件处理委托给特定的对象———
事件监听器:当该事件源发生指定的事情时,系统自动生成事件对象,并通知所委托的事
件监听器,由事件监听器相应的事件处理器处理这个事件。基于监听的事件处理模型如
图3-3所示。
图3-
3
基于监听的事件处理模型
当用户在HarmonyOS 控件上进行操作时,系统会自动生成事件对象,并将这个事件对
象以参数的形式传给注册到事件源上的事件监听器,事件监听器调用相应的事件处理来处
理。该过程类似于生活中我们每个人的能力都有限,当碰到一些自己处理不了的事情时,就
86
第3章 HarmonyOS事件处理
87
委托给某个机构处理。我们需要把所遇到的事情和要求描述清楚,这样,其他人才能比较好
地解决问题,然后该机构会选派具体的员工来处理这件事。其中,我们自己就是事件源,遇
到的事情就是事件,该机构就是事件监听器,具体解决事情的员工就是事件处理器。
单击事件的写法分别为定义实现类、使用当前类作为实现类、使用匿名内部类、使用
方法引用四种,接下来通过按钮的单击事件来展示监听事件的处理。
1.定义实现类
新建一个项目,在layout文件夹下打开ability_main.xml,定义一个按钮,详细代码
如程序清单3-1所示。
程序清单3-1:hmos\ch03\01\Listener1\entry\src\main\java\resource\base\layout\ability_main.xml
1 xml version="1.0" encoding="utf-8"? >
2
8
16
定义实现类实现单击事件,在MainAbilitySlice类中设计一个实现Component.
ClickedListener接口的MyLister类,降低代码耦合度,在该类中重写onClick()方法从而
满足单击事件需求,之后该类的方法可以在主类中的onStart()方法中被调用,详细代码
如程序清单3-2所示。
程序清单3-2:hmos\ch03\01\Listener1\entry\src\main\java\com\
example\listener1\slice\MainAbilitySlice.java
1 public class MainAbilitySlice extends AbilitySlice {
2 @Override
3 public void onStart(Intent intent) {
4 super.onStart(intent);
5 super.setUIContent(ResourceTable.Layout_ability_main);
6 Button button = (Button) findComponentById(ResourceTable.Id_
button1);
鸿蒙应用开发教程
88
7 button.setClickedListener((Component.ClickedListener) new
MyListener());
8 }
9 class MyListener implements Component.ClickedListener {
10 @Override
11 public void onClick(Component component) {
12 new ToastDialog(getContext())
13 .setText("按钮被单击了")
14 .show();
15 }
16 }
17 }
单击按钮屏幕下方会显示一个对话框,效果如图3-4所示。
图3-4 Button单击示例图
2.使用当前类作为实现类
使用当前类作为实现类实现单击事件,就是把上个例子中的MyListener类与主类合
二为一,减少代码量,详细代码如程序清单3-3所示,实现效果与图3-4相同。
程序清单3-3:hmos\ch03\01\Listener2\entry\src\main\java\com\
example\listener2\slice\MainAbilitySlice.java
1 public class MainAbilitySlice extends AbilitySlice implements Component.
ClickedListener{
2 @Override
第3章 HarmonyOS事件处理
89
3 public void onStart(Intent intent) {
4 super.onStart(intent);
5 super.setUIContent(ResourceTable.Layout_ability_main);
6 Button button = (Button) findComponentById(ResourceTable.Id_
button1);
7 button.setClickedListener(this);
8 }
9 @Override
10 public void onClick(Component component) {
11 new ToastDialog(getContext())
12 .setText("按钮被单击了")
13 .show();
14 }
15 }
3.使用匿名内部类
使用匿名内部类实现单击事件。匿名内部类,就是没有名字的一种嵌套类,使用匿名
内部类的方式,可以无须创建新的类,减少代码冗余,详细代码如程序清单3-4所示,实现
效果与图3-4相同。
程序清单3-4:hmos\ch03\01\Listener3\entry\src\main\java\com\
example\listener3\slice\MainAbilitySlice.java
1 public class MainAbilitySlice extends AbilitySlice {
2 @Override
3 public void onStart(Intent intent) {
4 super.onStart(intent);
5 super.setUIContent(ResourceTable.Layout_ability_main);
6 Button button = (Button) findComponentById(ResourceTable.Id_
button1);
7 button.setClickedListener(new Component.ClickedListener() {
8 @Override
9 public void onClick(Component component) {
10 new ToastDialog(getContext())
11 .setText("按钮被单击了")
12 .show();
13 }
14 });
15 }
16 }
4.使用方法引用
使用方法引用实现单击事件,详细代码如程序清单3-5 所示,实现效果与图3-4
相同。
鸿蒙应用开发教程
90
程序清单3-5:hmos\ch03\01\Listener4\entry\src\main\java\com\
example\listener4\slice\MainAbilitySlice.java
1 public class MainAbilitySlice extends AbilitySlice{
2 @Override
3 public void onStart(Intent intent) {
4 super.onStart(intent);
5 super.setUIContent(ResourceTable.Layout_ability_main);
6 Button button = (Button) findComponentById(ResourceTable.Id_
button1);
7 button.setClickedListener(this::onClick);
8 }
9 public void onClick(Component component){
10 new ToastDialog(getContext())
11 .setText("按钮被单击了")
12 .show();
13 }
14 }
3.2 HarmonyOS线程管理
进程是应用程序的执行实例,是程序的一次动态执行过程。动态过程是进程本身从
产生、发展到最终消亡的过程。线程是比进程更小的执行单位,进程可进一步细化为线
程,是一个程序内部的一条执行路径。
多线程是实现并发机制的一种有效手段,指一个进程在执行的过程中产生多个线程,
这些线程可以同时存在。一般出现以下3种情况需要使用多线程:①程序需要同时执行
两个或多个任务时;②程序需要实现一些需要等待的任务时,如用户输入、文件读写操作
时;③需要一些后台运行的程序时。
当应用启动时,系统会创建一个主线程。该线程随着应用的动态变化而创建和消亡,
是应用的核心线程。主线程负责处理大部分业务,负责应用和UI组件发生交互。所以,
主线程又称为UI线程。因为主线程在任何时候都有较高的响应速度,默认情况下所有
操作都在主线程上执行。如果需要执行比较耗时的操作,比如发起一条网络请求时,考虑
到网速等原因,服务器未必会立刻响应请求,可创建其他子线程运行这些操作,否则主线
程会因为耗时操作被阻塞,从而出现异常,从而影响用户对软件的正常使用。
3.2.1 线程管理接口说明
当应用的业务逻辑复杂时,需要处理多项任务,可能需要开发者创建多个线程来执行
多个任务。基于这种情况,代码复杂,难以维护,任务与线程的交互更加繁杂,导致运行效
率降低。为了解决这一问题,开发者可以使用TaskDispatcher分发不同的任务。
第3章 HarmonyOS事件处理
91
TaskDispatcher是一个任务分发器,它是Ability(Ability在本书4.1节有详细介绍)分发
任务的基本接口,采用了多线程技术,隐藏任务所在线程的实现细节,TaskDispatcher获取到
执行的任务和方法就可以自动创建合适的线程完成任务。
为保证应用有更好的响应性,需要设计任务的优先级。在UI线程上运行的任务默
认以高优先级运行,如果某个任务无须等待结果,则可以用低优先级。任务优先级如
表3-1所示。
表3-1 任务优先级
优 先 级详细描述
HIGH 最高任务优先级,比默认优先级、低优先级的任务有更高的概率执行
DEFAULT 默认任务优先级,比低优先级的任务有更高的概率执行
LOW 低任务优先级,比高优先级、默认优先级的任务有更低的概率执行
TaskDispatcher 具有不同的实现,每种实现对应不同的任务分发器。在分发任务时可
以指定任务的优先级,由同一个任务分发器分发出的任务具有相同的优先级。系统提供的
任务分发器有GlobalTaskDispatcher(全局并发任务分发器)、ParallelTaskDispatcher(并发任
务分发器)、SerialTaskDispatcher(串行任务分发器)、SpecTaskDispatcher(专有任务分发器)。
. GlobalTaskDispatcher由Ability 执行getGlobalTaskDispatcher(TaskPriority.
priority)获取,中文含义为全局并发任务分发器,priority参数代表任务的优先
级,可取值为HIGH、DEFAULT、LOW,全局并发任务分发器适用于任务没有联
系的情况。一个应用只有一个GlobalTaskDispatcher,它在程序结束时才被销
毁,具体代码如程序清单3-6所示。
程序清单3-6
1 TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher
(TaskPriority.DEFAULT);
. ParallelTaskDispatcher,它由Ability 执行的createParallelTaskDispatcher(String
name,TaskPriority.priority)创建并返回,中文含义为并发任务分发器,其中name
参数为任务分发器的名称,priority参数代表任务的优先级,可取值为HIGH、
DEFAULT、LOW,与GlobalTaskDispatcher 不同的是,ParallelTaskDispatcher 不具
有全局唯一性,可创建多个。开发者在创建或销毁dispatcher时,需要持有对应的
对象引用,具体代码如程序清单3-7所示。
程序清单3-7
1 String dispatcherName = "parallelTaskDispatcher";
2 TaskDispatcher parallelTaskDispatcher = createParallelTaskDispatcher
(dispatcherName, TaskPriority.DEFAULT);
鸿蒙应用开发教程
92
. SerialTaskDispatcher 是由Ability执行的createSerialTaskDispatcher(Stringname,
TaskPriority.priority)创建并返回的,中文含义为串行任务分发器,由该分发器分发
的所有任务都是按顺序执行的,但是执行这些任务的线程并不是固定的。如果要执
行并行任务,应使用ParallelTaskDispatcher 或者GlobalTaskDispatcher,而不是创建
多个SerialTaskDispatcher。如果任务之间没有依赖,应使用GlobalTaskDispatcher
来实现。它的创建和销毁由开发者自己管理,开发者在使用期间需要持有该对象引
用,具体代码如程序清单3-8所示。
程序清单3-8
1 String dispatcherName = "serialTaskDispatcher";
2 TaskDispatcher serialTaskDispatcher = createSerialTaskDispatcher
(dispatcherName, TaskPriority.DEFAULT);
. SpecTaskDispatcher的中文含义为专有任务分发器,它是绑定到专有线程上的任务
分发器。目前,专有线程是主线程。UITaskDispatcher和MainTaskDispatcher同属
SpecTaskDispatcher。官方更加推荐UITaskDispatcher。
. UITaskDispatcher:绑定到应用主线程的专有任务分发器,由getUITaskDispatcher()
创建并返回。由该分发器分发的所有任务都在主线程上按顺序执行,它在应用程序
结束时被销毁,具体代码如程序清单3-9所示。
程序清单3-9
1 TaskDispatcher uiTaskDispatcher = getUITaskDispatcher();
. MainTaskDispatcher:由Ability执行getMainTaskDispatcher()创建并返回,具
体代码如程序清单3-10所示。
程序清单3-10
1 TaskDispatcher mainTaskDispatcher= getMainTaskDispatcher();
3.2.2 线程管理开发步骤
1.syncDispatch(Runnablerunnable)
同步派发任务:派发任务并在当前线程等待任务执行时完成。在返回前,当前线程
会被阻塞。同步任务是那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个
任务执行完毕,才能执行后一个任务。使用时需实现Runnable接口的run()方法,在该
方法中定义需要执行的任务。
如程序清单3-11展示了如何使用GlobalTaskDispatcher派发同步任务。
第3章 HarmonyOS事件处理
93
程序清单3-11:hmos\ch03\02\syncGlobalTaskDispatcher\entry\src\main\java\com\
example\syncglobaltaskdispatcher\slice\MainAbilitySlice.java
1 static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "MY_TAG");
2 @Override
3 public void onStart(Intent intent) {
4 super.onStart(intent);
5 super.setUIContent(ResourceTable.Layout_ability_main);
6 TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher
(TaskPriority.DEFAULT);
7 globalTaskDispatcher.syncDispatch(new Runnable() {
8 @Override
9 public void run() {
10 HiLog.info(LABEL, "同步任务一启动");
11 }
12 });
13 HiLog.info(LABEL, "同步任务一结束");
14 globalTaskDispatcher.syncDispatch(new Runnable() {
15 @Override
16 public void run() {
17 HiLog.info(LABEL, "同步任务二启动");
18 }
19 });
20 HiLog.info(LABEL, "同步任务二结束");
21 globalTaskDispatcher.syncDispatch(new Runnable() {
22 @Override
23 public void run() {
24 HiLog.info(LABEL, "同步任务三启动");
25 }
26 });
27 HiLog.info(LABEL, "同步任务三结束");
28 }
29 //执行结果如下:
30 //同步任务一启动
31 //同步任务一结束
32 //同步任务二启动
33 //同步任务二结束
34 //同步任务三启动
35 //同步任务三结束
2.asyncDispatch(Runnablerunnable)
异步派发任务:派发任务,并立即返回,返回值是一个可用于取消任务的接口,需要
实现Runnable接口的run()方法,在该方法中定义需要执行的任务。与同步派发任务不
同的是,该任务不会阻塞后面代码的执行。
如下代码示例展示了如何使用GlobalTaskDispatcher派发异步任务,详细代码如程
序清单3-12所示。
鸿蒙应用开发教程
94
程序清单3-12:hmos\ch03\02\asyncGlobalTaskDispatcher\entry\src\main\java\com\
example\asyncGlobalTaskDispatcher\slice\MainAbilitySlice.java
1 public class MainAbilitySlice extends AbilitySlice {
2 static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "MY_TAG");
3 @Override
4 public void onStart(Intent intent) {
5 super.onStart(intent);
6 super.setUIContent(ResourceTable.Layout_ability_main);
7 TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher
(TaskPriority.DEFAULT);
8 globalTaskDispatcher.asyncDispatch(new Runnable() {
9 @Override
10 public void run() {
11 HiLog.info(LABEL, "async task1 run");
12 }
13 });
14 HiLog.info(LABEL, "after async task1");
15 }
16 }
17 //执行结果如下:
18 //after async task1
19 //async task1 run
3.delayDispatch(Runnablerunnable,longdelay)
异步延迟派发任务:异步执行,函数立即返回,内部会在延时指定时间后将任务派发
到相应队列中。该方法的第1个参数表示需执行的任务,第2个参数用于设置执行异步
任务延迟的时间。延时时间参数仅代表在这段时间以后任务分发器会将任务加入队列
中,任务的实际执行时间可能晚于这个时间。具体比这个数值晚多久,取决于队列及内部
线程池的繁忙情况。
如下代码示例展示了如何使用GlobalTaskDispatcher延迟派发任务,详细代码如程
序清单3-13所示。
程序清单3-13:hmos\ch03\02\delayGlobalTaskDispatcher\entry\src\main\java\com\
example\delayGlobalTaskDispatcher\slice\MainAbilitySlice.java
1 public class MainAbilitySlice extends AbilitySlice {
2 static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201,
"MY_TAG");
3 @Override
4 public void onStart(Intent intent) {
5 super.onStart(intent);
6 super.setUIContent(ResourceTable.Layout_ability_main);
7 long callTime = System.currentTimeMillis();
第3章 HarmonyOS事件处理
95
8 long delayTime = 500L;
9 TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher
(TaskPriority.DEFAULT);
10 Revocable revocable = globalTaskDispatcher.delayDispatch(new
Runnable() {
11 @Override
12 public void run() {
13 HiLog.info(LABEL, "delayDispatch task1 run");
14 final long actualDelay = System.currentTimeMillis() - callTime;
15 HiLog.info(LABEL, "actualDelayTime >= delayTime: %
{public}b", (actualDelay >= delayTime));
16 }
17 }, delayTime);
18 HiLog.info(LABEL, " delayDispatch task1 finish");
19 }
20 }
21 //执行结果可能如下:
22 //delayDispatch task1 finish
23 //delayDispatch task1 run
24 //actualDelayTime >= delayTime : true
4.Group
任务组:表示一组任务,且该组任务之间有一定的联系,由TaskDispatcher执行
createDispatchGroup创建并返回。通过asyncGroupDispatch(Groupgroup,Runnable
runnable)方法将任务加入分组中,通过groupDispatchNotify(Groupgroup,Runnable
runnable)方法可以设置该分组中的所有任务执行完成后再执行指定任务。
如下代码示例展示了任务组的使用方式:将一系列相关联的任务放入一个任务组,
执行完任务后关闭应用,详细代码如程序清单3-14所示。
程序清单3-14:hmos\ch03\02\Group\entry\src\main\java\com\
example\Group\slice\MainAbilitySlice.java
1 public class MainAbilitySlice extends AbilitySlice {
2 static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201,
"MY_TAG");
3 @Override
4 public void onStart(Intent intent) {
5 super.onStart(intent);
6 super.setUIContent(ResourceTable.Layout_ability_main);
7 String dispatcherName = "parallelTaskDispatcher";
8 TaskDispatcher dispatcher = createParallelTaskDispatcher
(dispatcherName, TaskPriority.DEFAULT);
9 //创建一个group 线程任务组
10 Group group = dispatcher.createDispatchGroup();
鸿蒙应用开发教程
96
11 //将任务1 加入线程任务组中,可以返回一个用于取消线程任务的接口
12 dispatcher.asyncGroupDispatch(group, new Runnable() {
13 @Override
14 public void run() {
15 HiLog.info(LABEL, " task1 is running");
16 }
17 });
18 //将与任务1 相关联的任务2 加入任务组
19 dispatcher.asyncGroupDispatch(group, new Runnable() {
20 @Override
21 public void run() {
22 HiLog.info(LABEL, " task2 is running");
23 }
24 });
25 //在任务组中的所有任务执行完成后执行指定任务
26 dispatcher.groupDispatchNotify(group, new Runnable() {
27 @Override
28 public void run() {
29 HiLog.info(LABEL, "the close task is running after all tasks in the group
are completed");
30 }
31 });
32 //执行结果可能如下:
33 //task1 is running
34 //task2 is running
35 //the close task is running after all tasks in the group are completed
36 //另外一种可能的执行结果:
37 //task2 is running
38 //task1 is running
39 //the close task is running after all tasks in the group are completed
40 }
41 }
5.Revocable
取消任务:Revocable是取消一个异步任务的接口。异步任务包括通过asyncDispatch、
delayDispatch、asyncGroupDispatch派发的任务。如果任务已经在执行中或已经执行完成,
则会返回取消失败的信息。
以下示例展示了如何取消一个异步延时任务,详细代码如程序清单3-15所示。
程序清单3-15:hmos\ch03\02\Revocable\entry\src\main\java\com\
example\Revocable\slice\MainAbilitySlice.java
1 public class MainAbilitySlice extends AbilitySlice {
2 static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201,
"MY_TAG");
第3章 HarmonyOS事件处理
97
3 @Override
4 public void onStart(Intent intent) {
5 super.onStart(intent);
6 super.setUIContent(ResourceTable.Layout_ability_main);
7 TaskDispatcher dispatcher = getUITaskDispatcher();
8 Revocable revocable = dispatcher.delayDispatch(new Runnable() {
9 @Override
10 public void run() {
11 HiLog.info(LABEL, "delay dispatch");
12 }
13 }, 10);
14 boolean revoked = revocable.revoke();
15 HiLog.info(LABEL, "%{public}b", revoked);
16 //一种可能的结果如下:
17 //true
18 }
19 }
6.syncDispatchBarrier
同步设置屏障任务:在任务组上设立任务执行屏障,同步等待任务组中的所有任务
执行完成,再执行指定任务。
以下示例展示了如何同步设置屏障,详细代码如程序清单3-16所示。
程序清单3-16:hmos\ch03\02\syncDispatchBarrier\entry\src\main\java\com\
example\syncDispatchBarrier\slice\MainAbilitySlice.java
1 public class MainAbilitySlice extends AbilitySlice {
2 static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201,
"MY_TAG");
3 @Override
4 public void onStart(Intent intent) {
5 super.onStart(intent);
6 super.setUIContent(ResourceTable.Layout_ability_main);
7 String dispatcherName = "parallelTaskDispatcher";
8 TaskDispatcher dispatcher = createParallelTaskDispatcher
(dispatcherName, TaskPriority.DEFAULT);
9 //创建任务组
10 Group group = dispatcher.createDispatchGroup();
11 //将任务加入任务组,返回一个用于取消任务的接口
12 dispatcher.asyncGroupDispatch(group, new Runnable() {
13 @Override
14 public void run() {
15 HiLog.info(LABEL, "task1 is running"); //1
鸿蒙应用开发教程
98
16 }
17 });
18 dispatcher.asyncGroupDispatch(group, new Runnable() {
19 @Override
20 public void run() {
21 HiLog.info(LABEL, "task2 is running"); //2
22 }
23 });
24 dispatcher.syncDispatchBarrier(new Runnable() {
25 @Override
26 public void run() {
27 HiLog.info(LABEL, "barrier"); //3
28 }
29 });
30 HiLog.info(LABEL, "after syncDispatchBarrier"); //4
31 //1 和2 的执行顺序不定;3 和4 总是在1 和2 之后按顺序执行
32 //可能的执行结果:
33 //task1 is running
34 //task2 is running
35 //barrier
36 //after syncDispatchBarrier
37
38 //另外一种执行结果:
39 //task2 is running
40 //task1 is running
41 //barrier
42 //after syncDispatchBarrier
43 }
44 }
7.asyncDispatchBarrier
异步设置屏障任务:在任务组上设立任务执行屏障后直接返回,指定任务将在任务
组中的所有任务执行完成后再执行。
以下示例展示了如何异步设置屏障,详细代码如程序清单3-17所示。
程序清单3-17:hmos\ch03\02\asyncDispatchBarrier\entry\src\main\java\com\
example\asyncDispatchBarrier\slice\MainAbilitySlice.java
1 public class MainAbilitySlice extends AbilitySlice {
2 static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201,
"MY_TAG");
3 @Override
4 public void onStart(Intent intent) {
第3章 HarmonyOS事件处理
99
5 super.onStart(intent);
6 super.setUIContent(ResourceTable.Layout_ability_main);
7 TaskDispatcher dispatcher = createParallelTaskDispatcher
("dispatcherName", TaskPriority.DEFAULT);
8 //创建任务组
9 Group group = dispatcher.createDispatchGroup();
10 //将任务加入任务组,返回一个用于取消任务的接口
11 dispatcher.asyncGroupDispatch(group, new Runnable() {
12 @Override
13 public void run() {
14 HiLog.info(LABEL, "task1 is running"); //1
15 }
16 });
17 dispatcher.asyncGroupDispatch(group, new Runnable() {
18 @Override
19 public void run() {
20 HiLog.info(LABEL, "task2 is running"); //2
21 }
22 });
23
24 dispatcher.asyncDispatchBarrier(new Runnable() {
25 @Override
26 public void run() {
27 HiLog.info(LABEL, "barrier"); //3
28 }
29 });
30 HiLog.info(LABEL, "after asyncDispatchBarrier"); //4
31 //1 和2 的执行顺序不定,但总在3 之前执行;4 不需要等待1、2、3 执行完成
32 //可能的执行结果:
33 //task1 is running
34 //task2 is running
35 //after asyncDispatchBarrier
36 //barrier
37 }
38 }
8.applyDispatch
执行多次任务:对指定任务执行多次。以下示例展示了如何执行多次任务,详细代
码如程序清单3-18所示。
程序清单3-18:hmos\ch03\02\applyDispatch\entry\src\main\java\com\
example\applyDispatch\slice\MainAbilitySlice.java
1 public class MainAbilitySlice extends AbilitySlice {
鸿蒙应用开发教程
1 00
2 static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201,
"MY_TAG");
3 @Override
4 public void onStart(Intent intent) {
5 super.onStart(intent);
6 super.setUIContent(ResourceTable.Layout_ability_main);
7 final int total = 10;
8 final CountDownLatch latch = new CountDownLatch(total);
9 final List indexList = new ArrayList<>(total);
10 TaskDispatcher dispatcher = getGlobalTaskDispatcher
(TaskPriority.DEFAULT);
11 //执行任务total 次
12 dispatcher.applyDispatch((index) -> {
13 indexList.add(index);
14 latch.countDown();
15 }, total);
16 //设置任务超时
17 try {
18 latch.await();
19 } catch (InterruptedException exception) {
20 HiLog.error(LABEL, "latch exception");
21 }
22 HiLog.info(LABEL, "list size matches, %{public}b", (total ==
indexList.size()));
23 //执行结果:
24 //list size matches, true
25 }
26 }
3.3 HarmonyOS线程间通信
3.3.1 线程间通信场景介绍
在开发过程中,经常需要在当前线程中处理耗时的操作,比如联网读取数据,或者读
取本地较大的一个文件,不能把这些操作放在主线程中,因为放在主线程中,界面会出现
假死现象,但是又不希望当前的线程受到阻塞,所以会在子线程中执行耗时操作。并且
UI控件不是线程安全的,所以系统不允许子线程更新UI,之后提交请求将子线程的数据
传递给主线程,让主线程做UI更新。此时就可以使用EventHandler机制。
EventHandler是HarmonyOS用于处理线程间通信的一种机制,可以在多个线程并
发更新UI的同时保证线程安全,可以通过EventRunner创建新线程,将耗时的操作放到
新线程上执行。在不阻塞原来的线程基础下,合理地处理任务。当熟悉了EventHandler
第3章HarmonyOS事件处理
的原理之后我们知道,EventHandler不仅能将子线程的数据传递给主线程,它还能实现
任意两个线程的数据传递。
EventRunner是一种事件循环器,循环从该EventRunner创建的新线程的事件队列
中获取InnerEvent事件或者Runnable任务。InnerEvent是EventHandler投递的事件。
EventHandler是一种用户在当前线程上投递InnerEvent事件或者Runnable任务到
异步线程上处理的机制。每一个EventHandler和指定的EventRunner所创建的新线程
绑定,并且该新线程内部有一个事件队列。EventHandler可以投递指定的InnerEvent事
件或Runnable任务到这个事件队列。EventRunner从事件队列里循环地取出事件,如果
取出的事件是InnerEvent事件,将在EventRunner所在线程执行procesEvent回调;如
果取出的事件是Runnable任务,将在EventRunner所在线程执行Runnable的run回调。
一般地,EventHandler主要有两个作用:①在不同线程间分发和处理InnerEvent事件或
Runnable任务;②延迟处理InnerEvent事件或Runnable任务。EventHandler的运作
机制如图3-5所示。
图3-
5
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的主要功能是将InnerEvent事件或者Runnable任务投递到其他线程
进行处理,其使用场景包括:
101
鸿蒙应用开发教程
(1)开发者需要将InnerEvent事件投递到新的线程,按照优先级和延时进行处理。
投递时,EventHandler的优先级可在IMMEDIATE 、HIGH 、LOW 、IDLE中选择,并设置
合适的delayTime 。
(2)开发者需要将Runnable任务投递到新的线程,并按照优先级和延时进行处理。
投递时,EventHandler的优先级可在IMMEDIATE 、HIGH 、LOW 、IDLE中选择,并设置
合适的delayTime 。
(3)开发者需要在新创建的线程里投递事件到原线程进行处理。
EventRunner的工作模式可以分为托管模式和手动模式。两种模式是在调用
EventRunner的create()方法时,通过选择不同的参数来实现的,详见API参考。默认为
托管模式。
托管模式:不需要开发者调用run()和stop()方法启动和停止EventRunner。当
EventRunner实例化时,系统调用run()启动EventRunner;当EventRunner不被引用
时,系统调用stop()停止EventRunner。
手动模式:需要开发者自行调用EventRunner的run()方法和stop()方法确保线程
的启动和停止。
3.2
线程间通信接口介绍
3.
EventHandler的属性Priority(优先级)介绍:EventRunner将根据优先级的高低从
事件队列中获取事件或者Runnable任务进行处理。
EventHandler的属性如表3-2所示。
表3-
2
EventHandler的属性
属性详细描述
Priority.IMMEDIATE 表示事件被立即投递
Priority.HIGH 表示事件先于LOW优先级投递
Priority.LOW 表示事件先于IDLE优先级投递,事件的默认优先级是LOW
Priority.IDLE 表示在没有其他事件的情况下才投递该事件
EventHandler的主要接口如表3-3所示。
表3-
3
EventHandler的主要接口
接口名详细描述
EventHandler(EventRunnerrunner) 利用已有的EventRunner创建EventHandler
curent() 在procesEvent回调中获取当前的EventHandler
procesEvent(InnerEventevent) 回调处理事件,由开发者实现
sendEvent(InnerEventevent,longdelayTime) 发送一个延时事件到事件队列,优先级为LOW
sendEvent(InnerEventevent,longdelayTime,
EventHandler.Prioritypriority) 发送一个指定优先级的延时事件到事件队列
102
第3章HarmonyOS事件处理
续表
接口名详细描述
postSyncTask(Runnabletask,EventHandler.
Prioritypriority)
发送一个指定优先级的Runnable同步任务到事件队
列,延时为0ms
postTask(Runnabletask,long delayTime,
EventHandler.Prioritypriority)
发送一个指定优先级的Runnable延时任务到事件
队列
sendTimingEvent (InnerEvent event,longtaskTime,EventHandler.Prioritypriority)
发送一个带优先级的事件到队列,在taskTime时间
执行,如果taskTime小于当前时间,则立即执行
postTimingTask (Runnable task, longtaskTime,EventHandler.Prioritypriority)
发送一个带优先级的Runnable任务到队列,在
taskTime时间执行,如果taskTime小于当前时间,
则立即执行
removeEvent(inteventId,longparam,Object
object) 删除指定id、param和object的事件
EventRunner的主要接口如表3-4所示。
表3-
4
EventRunner的主要接口
接口名详细描述
create() 创建一个拥有新线程的EventRunner
create(booleaninNewThread)
创建一个拥有新线程的EventRunner,参数为true时,EventRunner
为托管模式,系统自动管EventRunner;参数为false时,EventRunner
为手动模式
create(StringnewThreadName) 创建一个拥有新线程的EventRunner,新线程的名字是newThreadName
curent() 获取当前线程的EventRunner
run() EventRunner为手动模式时,调用该方法启动新的线程
stop() EventRunner为手动模式时,调用该方法停止新的线程
InnerEvent的主要接口如表3-5所示。
表3-
5
InnerEvent的主要接口
接口名详细描述
drop() 释放一个事件实例
get() 获得一个事件实例
get(inteventId) 获得一个指定的eventId的事件实例
get(inteventId,longparam) 获得一个指定的eventId和param的事件实例
get(inteventId,longparam,Objectobget(inteventId,Objectobject)
ject) 获得一个指定的eventId、param和object的事件实例
获得一个指定的eventId和object的事件实例
PacMapgetPacMap() 获取PacMap,如果没有,就新建一个
103