HarmonyOS 线程讲解(任务分发、线程通信)

news2024/12/25 22:28:07

一、简单说明

说起鸿蒙的线程就不得不说Android的线程,相信都知道在Android中,每一个应用都会有自己的主线程和其他的子线程,主线程负责处理大部分业务,负责UI的显示和更新等操作,所以又称之为UI线程,同时,我们还知道不能够在UI线程中进行一些耗时操作,比如网络访问、数据读写等一些功能会启动一个子线程,或者异步线程来进行,这样做可以避免出现程序无法响应这样的情况,也就是ANR(Android Not Response)。当然这是造成程序ANR的原因之一,还有一些其他原因,比如系统逻辑,空指针,内存溢出。那么我说了这么多关于Android中线程使用的说明是为了什么呢?当然是抛砖引玉,就是说明鸿蒙的线程和Android的线程是异父异母的亲兄弟啊!所以刚才我说的以上用法在鸿蒙中同样可行,这个解释你满不满意呢?(PS:不满意也不用动手,都是文明人,键盘不饶人。)

二、任务分发器

如果刚才的内容你都知道的话,那么下面的内容你就不一定也知道,如果你知道,就当我没说。下面我们从实际的场景来着手说明,例如自动更新:应用在进入首页时需要通过访问后台得到最新的应用版本,同时将版本信息保存到本地,当版本不一致时可以选择下载安装更新,但是更新又是在后台进行的,因为你不能够让用户等你更新完再使用。这样一个流程要在Android里面实现也不是什么难事,同一时间执行多个任务且不在主线程执行,也许我这个比方打的不是很好,你应该能懂我的意思。因为这个业务还比较的简单,举这个例子就是为了说明TaskDispatcher (音译:他死 diss 怕球儿~),注意这个儿化音,划重点,要考的。后面三个字说的要快,并且带点伦敦郊区的口音,只要你的英语说的够快,别人就不知道你说的是中文。天下武功,唯快不破。

TaskDispatcher 是一个任务分发器,它是 Ability 分发任务的基本接口,隐藏任务所在线程的实现细节。为保证应用有更好的响应性,我们需要设计任务的优先级。在 UI 线程上运行的任务默认以高优先级运行,如果某个任务无需等待结果,则可以用低优先级。级别如下

HIGH 最高任务优先级,比默认优先级、低优先级的任务有更高的几率得到执行。

DEFAULT 默认任务优先级, 比低优先级的任务有更高的几率得到执行。

LOW 低任务优先级,比高优先级、默认优先级的任务有更低的几率得到执行。

TaskDispatcher 具有多种实现,每种实现对应不同的任务分发器。在分发任务时可以指定任务的优先级,由同一个任务分发器分发出的任务具有相同的优先级。系统提供的任务分发器有 GlobalTaskDispatcher、ParallelTaskDispatcher、SerialTaskDispatcher 、SpecTaskDispatcher。为了方便演示代码我新建一个手表的鸿蒙项目,如下图所示:

① GlobalTaskDispatcher

全局并发任务分发器,(音译:狗萝卜 他死 diss 怕球儿~,解说:前面三个字要读的比后面三个字更快才行,有难度的)由 Ability 执行 getGlobalTaskDispatcher()获取。适用于任务之间没有联系的情况。一个应用只有一个 GlobalTaskDispatcher,它在程序结束时才被销毁。

  /**     * priority  优先级     **/    
public TaskDispatcher getGlobalTaskDispatcher(TaskPriority priority) {  
      throw new RuntimeException("Stub!");  
  }

我在MainAbility中通过调用getGlobalTaskDispatcher()方法,传入一个等级TaskPriority.DEFAULT,这个也可以用null来代替。然后得到一个TaskDispatcher的对象

② ParallelTaskDispatcher

并发任务分发器,(音译:佩尔楼 他死 diss 怕球儿~)由 Ability 执行 createParallelTaskDispatcher()创建并返回。与GlobalTaskDispatcher 不同的是,ParallelTaskDispatcher 不具有全局唯一性,可以创建多个。开发者在创建或销毁 dispatcher 时,需要持有对应的对象引用 。

/**     * 创建并发任务分发器   
* @param name  名称    
* @param priority  优先级     
* @return parallelTaskDispatcher     
*/    

public TaskDispatcher createParallelTaskDispatcher(String name, TaskPriority priority) {
        throw new RuntimeException("Stub!"); 
   }

创建时传两个参数,一个字符串的名字,一个是等级优先级,这里我用null也可以的。这里你又可以想一下为什么需要多一个名字参数,当然是为了区别不同的并发任务分布器,不过至于可不可以重名我就不知道了,理论上来说,应该不允许重名,说不定会给你报错,可以自己去尝试。

③ SerialTaskDispatcher

串行任务分发器(音译:C瑞尔 他死 diss 怕球儿~)由 Ability 执行 createSerialTaskDispatcher()创建并返回。由该分发器分发的所有的任务都是按顺序执行,但是执行这些任务的线程并不是固定的。如果要执行并行任务,应使用 ParallelTaskDispatcher 或者 GlobalTaskDispatcher,而不是创建多个SerialTaskDispatcher。如果任务之间没有依赖,应使用 GlobalTaskDispatcher 来实现。它的创建和销毁由开发者自己管理,开发者在使用期间需要持有该对象引用。你可以把它理解成一个有序列表,但是列表里面的值是可变的。它的方法如下:

/**     
* 创建串行任务分发器     
* @param name 名字     
* @param priority 优先级     
* @return SerialTaskDispatcher     
*/    
public TaskDispatcher createSerialTaskDispatcher(String name, TaskPriority priority) {
        throw new RuntimeException("Stub!");  
  }

获取方法

④ SpecTaskDispatcher

专有任务分发器(音译:思拜客 他死 diss 怕球儿~) 专有任务分发器,绑定到专有线程上的任务分发器。目前已有的专有线程是主线程。

UITaskDispatcher 和 MainTaskDispatcher 都属于 SpecTaskDispatcher。建议使用UITaskDispatcher。

UITaskDispatcher:绑定到应用主线程的专有任务分发器, 由 Ability 执行getUITaskDispatcher()创建并返回。 由该分发器分发的所有的任务都是在主线程上按顺序执行,它在应用程序结束时被销毁。之前我们说主线程上不能执行过多的任务,那么这个分发器就可以绑定到主线程上,进而可以执行很多任务,你可以这么理解。方法如下:

 \*\* \* 获取主线程任务分发器 \
* @return MainTaskDispatcher \
*/ 
public final TaskDispatcher getMainTaskDispatcher() { 
throw new RuntimeException("Stub!"); 
} 
\*\* \* 获取UI线程任务分发器 \
* @return UITaskDispatcher \
*/
public final TaskDispatcher getUITaskDispatcher() {
 throw new RuntimeException("Stub!"); 
}

⑤ 同步派发任务(syncDispatch)

就是一个同步进行的线程,下面由globalTaskDispatcher来派发同步任务。

修改MainAbility。

//日志private static final HiLogLabel label = new HiLogLabel(3, 0xD001100, "ThreadDemo");
private TaskDispatcher globalTaskDispatcher;
@Override    public void onStart(Intent intent) {        super.onStart(intent); 
       super.setMainRoute(MainAbilitySlice.class.getName());        
//获取全局并发任务分发器  一个应用程序只有一个        globalTaskDispatcher = getGlobalTaskDispatcher(TaskPriority.DEFAULT); 
       //GlobalTaskDispatcher 派发同步任务        
syncTask();   
}

syncTask方法中中派发三个同步任务,打印日志

\*\* \* GlobalTaskDispatcher 派发同步任务 \*/ 

private void syncTask() { 
globalTaskDispatcher.syncDispatch(new Runnable() { 
@Override public void run() {
 HiLog.info(label, "sync task1 run"); 
}
 }
); 
HiLog.info(label, "after sync task1"); globalTaskDispatcher.syncDispatch(new Runnable() { 
@Override public void run() {
 HiLog.info(label,"sync task2 run"); 
    } 
  }
); 
HiLog.info(label,"after sync task2"); globalTaskDispatcher.syncDispatch(new Runnable() {
 @Override public void run() {
 HiLog.info(label,"sync task3 run"); 
    }
   }
); 
HiLog.info(label,"after sync task3"); 
}

运行之后,日志如下所示:

虽然现在是没有问题,但是如果对 syncDispatch 使用不当, 将会导致死锁。如下情形可能导致死锁发生:

在专有线程上,利用该专有任务分发器进行 syncDispatch。

在被某个串行任务分发器(dispatcher_a)派发的任务中,再次利用同一个串行任务分发器(dispatcher_a)对象派发任务。

被某个串行任务分发器(dispatcher_a)派发的任务中,经过数次派发任务,最终又利用该(dispatcher_a)串行任务分发器派发任务。例如:dispatcher_a 派发的任务使用 dispatcher_b 进行任务的派发,在 dispatcher_b 派发的任务中又利用 dispatcher_a 进行派发任务。

行任务分发器(dispatcher_a)派发的任务中利用串行任务分发器(dispatcher_b)进行同步派发任务,同时dispatcher_b 派发的任务中利用串行任务分发器(dispatcher_a)进行同步派发任务。在特定的线程执行顺序下将导致死锁。

⑥ 异步派发任务(asyncDispatch)

写一个asyncTask方法,里面的内容和syncTask差不多。

 /\*\* \* GlobalTaskDispatcher 派发同步任务 
\*/ 
private void asyncTask() { 
globalTaskDispatcher.asyncDispatch(new Runnable() { 
@Override public void run() { 
HiLog.info(label,"sync task1 run"); 
  } 
}  ); 
HiLog.info(label,"after sync task1"); 
}

然后在onStart中调用即可

然后再运行。

同时这个异步派发是任务是可以取消的。Revocable,globalTaskDispatcher.asyncDispatch的返回值就是Revocable,

那么上面async中的代码就可以这样写。如下所示:

    /**     * GlobalTaskDispatcher 派发同步任务     */    private void asyncTask() {
        Revocable revocable = globalTaskDispatcher.asyncDispatch(new Runnable() { 
           @Override            public void run() {
                HiLog.info(label, "async task1 run");
            }
        });        //撤销任务分发
        revocable.revoke();
        HiLog.info(label, "after async task1"); 
   }

在运行一下:

很明显里面的任务已经撤销了

⑦ 异步延迟派发任务(delayDispatch)

异步延迟派发任务:异步执行,函数立即返回,内部会在延时指定时间后将任务派发到相应队列中。延时时间参数仅代表在这段时间以后任务分发器会将任务加入到队列中,任务的实际执行时间可能晚于这个时间。具体比这个数值晚多久,取决于队列及内部线程池的繁忙情况。下面用代码演示一下:

    /**     * 当前时间     */    
final long currentTime = System.currentTimeMillis();    /**     * 延迟时间 100ms     */    
final long delayTime = 100;

delayTask方法如下:

 /\*\* \* GlobalTaskDispatcher 派发异步延时任务 \*/ 
private void delayTask() {
 globalTaskDispatcher.delayDispatch(new Runnable() {
 @Override public void run() {
 HiLog.info(label, "delay task1 run");
 final long actualDelayMs = System.currentTimeMillis() - currentTime; HiLog.info(label,"actualDelayTime >= delayTime : " + (actualDelayMs >= delayTime));
   }
   },delayTime); HiLog.info(label, "after delay task1");
 }

日志如下:

⑧ 任务组(Group)

表示一组任务,且该组任务之间有一定的联系,由 TaskDispatcher 执行createDispatchGroup 创建并返回,代码如下所示。

 /\*\* \* 任务组 \*/ 
private void taskGroup(Context context) {
 //创建并发任务分发器 
TaskDispatcher dispatcher = context.createParallelTaskDispatcher("parallelTaskDispatcher", TaskPriority.DEFAULT); 
//创建任务组
 Group group = dispatcher.createDispatchGroup();
 // 将任务 1 加入任务组,
 dispatcher.asyncGroupDispatch(group,new Runnable() {
 @Override public void run() {
 HiLog.info(label,"download task1 is running");
 }
 }
); // 将任务 2 加入任务组, dispatcher.asyncGroupDispatch(group, new Runnable() {
 @Override public void run() {
 HiLog.info(label,"download task2 is running"); } }); // 在任务组中的所有任务执行完成后执行指定任务。 
dispatcher.groupDispatchNotify(group, new Runnable() {
 @Override public void run() {
 HiLog.info(label, "the close task is running after all tasks in the group are completed");
     } 
}); 
}

onStart方法中调用

运行效果如下,我运行了两次,两次日志是不一样的,如果里面执行的业务是不一样的,则耗时长的后完成。

⑨ 取消任务(Revocable)

Revocable 是取消一个异步任务的接口。异步任务包括通过 asyncDispatch、delayDispatch、asyncGroupDispatch 派发的任务。如果任务已经在执行中或执行完成,则会返回取消失败。

    /**     * 取消异步任务     */
    private void revocableAsyncTask(Context context) {
        TaskDispatcher dispatcher = context.getUITaskDispatcher();
        Revocable revocable = dispatcher.delayDispatch(new Runnable() {
            @Override
     public void run() {
                HiLog.info(label, "delay dispatch"); 
           } 
       }, 
10);
        boolean revoked = revocable.revoke();        HiLog.info(label, "" + revoked);   
 }

在onStart()中调用

//取消异步任务revocableAsyncTask(MainAbility.this);

运行效果如下:

⑩ 同步设置屏障任务 (syncDispatchBarrier)

在任务组上设立任务执行屏障,同步等待任务组中的所有任务执行完成,再执行指定任务。在全局并发任务分发器(GlobalTaskDispatcher)上同步设置任务屏障,将不会起到屏障作用。就比如说考试提前交卷,A提前20分钟,B提前10分钟,但你只是离开了考室,离不开考场,这个考场的门就是这个屏障,无论你是提前多久,你都要等到所有考生考完,老师收卷之后才能离开考场,就是这么一个理。代码如下:

 /\*\* \* 同步设置屏障任务 \*/ 
private void setSyncDispatchBarrier(Context context) {
 TaskDispatcher dispatcher  = context.createParallelTaskDispatcher("parallelTask",TaskPriority.DEFAULT); 
//创建任务组 
Group group = dispatcher.createDispatchGroup(); //将任务1加入任务组 
dispatcher.asyncGroupDispatch(group, new Runnable() {
 @Override public void run() {
 HiLog.info(label,"task1 is running");
     } 
}); //将任务2加入任务组 
dispatcher.asyncGroupDispatch(group, new Runnable() {
   @Override public void run() {
 HiLog.info(label,"task2 is running"); 
  } 
}); 
dispatcher.asyncDispatchBarrier(new Runnable() {
 @Override public void run() {
   HiLog.info(label,"Barrier");
   }
 }); 
HiLog.info(label,"after syncDispatchBarrier"); 
}

在onStart()中调用,

//同步设置屏障任务setSyncDispatchBarrier(MainAbility.this);

运行

⑪ 异步设置屏障任务 (asyncDispatchBarrier)

在任务组上设立任务执行屏障后直接返回,指定任务将在任务组中的所有任务执行完成后再执行。在全局并发任务分发器(GlobalTaskDispatcher)上异步设置任务屏障,将不会起到屏障作用。可以使用并发任务分发器(ParallelTaskDispatcher)分离不同的任务组,达到微观并行、宏观串行的行为。

 /\*\* \* 异步设置屏障任务 \*/ 
private void setAsyncDispatchBarrier(Context context) {
 TaskDispatcher dispatcher = context.createParallelTaskDispatcher("AsyncParallelTaskDispatcher", TaskPriority.DEFAULT); // 创建任务组 
Group group = dispatcher.createDispatchGroup(); //将任务1加入任务组 
dispatcher.asyncGroupDispatch(group, new Runnable() {
 @Override public void run() {
 HiLog.info(label, "task1 is running"); // 1 
} 
}); 
//将任务2加入任务组 dispatcher.asyncGroupDispatch(group, new Runnable() {
 @Override public void run() {
 HiLog.info(label, "task2 is running"); // 2 
} }); 
dispatcher.asyncDispatchBarrier(new Runnable() { 
@Override public void run() {
 HiLog.info(label, "barrier"); // 3 
} });
 HiLog.info(label, "after syncDispatchBarrier"); // 4
 }

在onStart()中调用

//异步设置屏障任务setAsyncDispatchBarrier(MainAbility.this);

运行效果

⑫ 执行多次任务(applyDispatch)

对指定任务执行多次

/\*\* \* 执行多次任务 \*/ p
rivate void multipleTasks() { //总执行次数 
final int total = 10; //倒数计时器 
final CountDownLatch latch = new CountDownLatch(total); //长整形列表数组 
final ArrayList<Long> indexList = new ArrayList<>(total); TaskDispatcher dispatcher = createParallelTaskDispatcher("dispatcher", TaskPriority.DEFAULT); //执行多次任务 
dispatcher.applyDispatch((index) -> {
 indexList.add(index); latch.countDown();
 HiLog.info(label, "long: "+index); }, 
total); 
//设置任务超时 
try { latch.await(); } 
catch (InterruptedException e) {
 HiLog.info(label, "latch exception"); 
} //返回true则说明执行了10次 
HiLog.info(label, "list size matches," + (total == indexList.size())); 
}

运行效果如下:

三、线程通信

在开发过程中,开发者经常需要在当前线程中处理下载任务等较为耗时的操作,但是又不希望当前的线程受到阻塞。此时,就可以使用 EventHandler 机制。EventHandler 是HarmonyOS 用于处理线程间通信的一种机制,可以通过 EventRunner 创建新线程,将耗时的操作放到新线程上执行。这样既不阻塞原来的线程,任务又可以得到合理的处理。比如:主线程使用 EventHandler 创建子线程,子线程做耗时的下载图片操作,下载完成后,子线程通过 EventHandler 通知主线程,主线程再更新 UI。

① EventRunner

EventRunner 是一种事件循环器,循环处理从该 EventRunner 创建的新线程的事件队列中获取 InnerEvent 事件或者 Runnable 任务。InnerEvent 是 EventHandler 投递的事件。

EventHandler 是一种用户在当前线程上投递 InnerEvent 事件或者 Runnable 任务到异步线程上处理的机制。每一个 EventHandler 和指定的 EventRunner 所创建的新线程绑定,并且该新线程内部有一个事件队列。EventHandler 可以投递指定的 InnerEvent 事件或 Runnable 任务到这个事件队列。EventRunner 从事件队列里循环地取出事件,如果取出的事件是InnerEvent 事件,将在 EventRunner 所在线程执行 processEvent 回调;如果取出的事件是Runnable 任务,将在 EventRunner 所在线程执行 Runnable 的 run 回调。

② EventHandler

EventHandler 有两个主要作用:

在不同线程间分发和处理 InnerEvent 事件或 Runnable 任务。

延迟处理 InnerEvent 事件或 Runnable 任务。

EventHandler 的运作机制如下图所示:

使用 EventHandler 实现线程间通信的主要流程:

EventHandler 投递具体的 InnerEvent 事件或者 Runnable 任务到 EventRunner 所创建的线程的事件队列。

EventRunner 循环从事件队列中获取 InnerEvent 事件或者 Runnable 任务。

处理事件或任务:

① 如果 EventRunner 取出的事件为 InnerEvent 事件,则触发 EventHandler 的回调方法并触发 EventHandler 的处理方法,在新线程上处理该事件。

② 如果 EventRunner 取出的事件为 Runnable 任务,则 EventRunner 直接在新线程上处理 Runnable 任务。

1. 约束限制

在进行线程间通信的时候,EventHandler 只能和 EventRunner 所创建的线程进行绑定,EventRunner 创建时需要判断是否创建成功,只有确保获取的 EventRunner 实例非空时,才可以使用 EventHandler 绑定 EventRunner。

一个 EventHandler 只能同时与一个 EventRunner 绑定,一个 EventRunner 上可以创建多个 EventHandler。

2. 开发场景

EventHandler 的主要功能是将 InnerEvent 事件或者 Runnable 任务投递到其他的线程进行处理,其使用的场景包括

开发者需要将 InnerEvent 事件投递到新的线程,按照优先级和延时进行处理。投递时,EventHandler 的优先级可在IMMEDIATE、HIGH、LOW、IDLE 中选择,并设置合适的 delayTime。

开发者需要将 Runnable 任务投递到新的线程,并按照优先级和延时进行处理。投递时,EventHandler 的优先级可在IMMEDIATE、HIGH、LOW、IDLE 中选择,并设置合适的 delayTime。

开发者需要在新创建的线程里投递事件到原线程进行处理。

3. 工作模式

EventRunner 的工作模式可以分为托管模式和手动模式。两种模式是在调用 EventRunner 的create()方法时,通过选择不同的参数来实现的,默认为托管模式。

托管模式:不需要开发者调用 run()和 stop()方法去启动和停止 EventRunner。当 EventRunner 实例化时,系统调用

run()来启动 EventRunner;当 EventRunner 不被引用时,系统调用 stop()来停止 EventRunner。

手动模式:需要开发者自行调用 EventRunner 的 run()方法和 stop()方法来确保线程的启动和停止。

③ 接口说明

1. EventHandler

EventHandler 的属性 Priority(优先级)介绍:

EventRunner 将根据优先级的高低从事件队列中获取事件或者 Runnable 任务进行处理。

属性描述
Priority.IMMEDIATE表示事件被立即投递
Priority.HIGH表示事件先于 LOW 优先级投递
Priority.LOW表示事件优于 IDLE 优先级投递,事件的默认优先级是 LOW
Priority.IDLE表示在没有其他事件的情况下,才投递该事件
  • EventHandler 的主要接口介绍:

2. EventRunner
  • EventRunner 的主要接口介绍:

3. InnerEvent
  • InnerEvent 的属性介绍:
属性描述
eventId事件的 ID, 由开发者定义用来辨别事件
object事件携带的 Object 信息
param事件携带的 long 型数据
  • InnerEvent 的主要接口介绍:

④ 开发步骤

一、EventHandler 投递 InnerEvent 事件

EventHandler 投递 InnerEvent 事件,并按照优先级和延时进行处理,创建一个MyEventHandler类型继承EventHandler

package com.llw.thread.event;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
public class MyEventHandler extends EventHandler {
 /\*\* \* 日志 \*/ 
private static final HiLogLabel label = new HiLogLabel(3, 0xD001100, "ThreadDemo");
 public MyEventHandler(EventRunner runner) 
throws IllegalArgumentException { super(runner); 
}
 /\*\* \* 1. 创建 EventHandler 的子类,在子类中重写实现方法 processEvent()来处理事件 \* 
\* @param event \*/ 
@Override 
protected void processEvent(InnerEvent event) {
 super.processEvent(event);
 if (event == null) { return; }
 int eventId = event.eventId;
 long param = event.param; 
switch ((int) (eventId | param)) {
 case 1: HiLog.info(label, "eventId | param --->" + 1); 
  break; 
default: break; } 
} /\*\* \* EventHandler 投递 InnerEvent 事件 \*/ 
private void initInnerEvent() {
 //2. 创建 EventRunner,以手动模式为例。 
// create()的参数是 true 时,则为托管模式 
EventRunner runner = EventRunner.create(false);
 if (runner == null) { return; }   //3. 创建 EventHandler 子类的实例。 
MyEventHandler myHandler = new MyEventHandler(runner); //4. 获取 InnerEvent 事件。 
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); //5. 投递事件,投递的优先级以 IMMEDIATE 为例,延时选择 0ms 和 2ms。 
myHandler.sendEvent(event1, 0, EventHandler.Priority.IMMEDIATE); // 延时 2ms 后立即处理 
myHandler.sendEvent(event2, 2, EventHandler.Priority.IMMEDIATE); //6. 启动和停止 EventRunner,如果为托管模式,则不需要此步骤。 
runner.run(); //待执行操作 
// ... 执行业务逻辑
 // 停止
 EventRunner runner.stop(); 
}
}
二、EventHandler 投递 Runnable 任务
 /\*\* \* EventHandler 投递 Runnable 任务 \*/ 
private void initRunnable() {
 //2. 创建 EventRunner,以手动模式为例。
 // create()的参数是 true 时,则为托管模式 
EventRunner runner = EventRunner.create(false); if (runner == null) { return; } 
//3. 创建 EventHandler 子类的实例。
MyEventHandler myHandler = new MyEventHandler(runner); 
//2. 创建 Runnable 任务。 
Runnable task1 = new Runnable() {
 @Override public void run() { HiLog.info(label, "task1 running"); } };
 Runnable task2 = new Runnable() {
 @Override public void run() {
 HiLog.info(label, "task1 running"); 
  } 
}; //3. 投递 Runnable 任务,投递的优先级以 IMMEDIATE 为例,延时选择 0ms 和 2ms。 
myHandler.postTask(task1, 0, EventHandler.Priority.IMMEDIATE); // 延时 2ms 后立即执行 
myHandler.postTask(task2, 2, EventHandler.Priority.IMMEDIATE); //4. 启动和停止 EventRunner,如果是托管模式,则不需要此步骤。 runner.run(); 
//待执行操作 
// ... 执行业务逻辑 
// 停止
 EventRunner runner.stop();
 }
三、在新创建的线程里投递事件到原线程

修改MyEventHandler之后代码如下:

package com.llw.thread.event;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
public class MyEventHandler extends EventHandler {
 /\*\* \* 日志 \*/ 
private static final HiLogLabel label = new HiLogLabel(3, 0xD001100, "ThreadDemo");
 private MyEventHandler myHandler; 
private EventRunner runner;
 public MyEventHandler(EventRunner runner) throws IllegalArgumentException {
 super(runner); } /\*\* \* 1. 创建 EventHandler 的子类,在子类中重写实现方法 processEvent()来处理事件 \* \* @param event \*/ @Override 
protected void processEvent(InnerEvent event) {
 super.processEvent(event); 
if (event == null) { return; }
 int eventId = event.eventId;
 long param = event.param; 
Object object = event.object; switch ((int) (eventId | param)) {
   case 1: HiLog.info(label, "eventId | param --->" + 1); 
break;
 case 2: EventRunner runner2 = null; // 将原先线程的 EventRunner 实例投递给新创建的线程
 if (object instanceof EventRunner) {
 //强转 runner2 = (EventRunner) object;
 } // 将原先线程的 EventRunner 实例与新创建的线程的 EventHandler 绑定
 EventHandler myHandler2 = new EventHandler(runner2) {
 @Override protected void processEvent(InnerEvent event) {
 //需要在原先线程执行的操作
 } 
};
 int eventId2 = 1; long param2 = 0; 
Object object2 = null;
 InnerEvent event2 = InnerEvent.get(eventId2, param2, object2); // 投递事件到原先的线程
myHandler2.sendEvent(event2);
 break; default: break; }
 } /\*\* \* EventHandler 投递 InnerEvent 事件 \*/ 
private void initInnerEvent() {
 //2. 创建 EventRunner,以手动模式为例。 // create()的参数是 true 时,则为托管模式
 runner = EventRunner.create(false);
 if (runner == null) { return; } //3. 创建 EventHandler 子类的实例。
 myHandler = new MyEventHandler(runner);
 //4. 获取 InnerEvent 事件。
 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); 
//5. 投递事件,投递的优先级以 IMMEDIATE 为例,延时选择 0ms 和 2ms。 
myHandler.sendEvent(event1, 0, EventHandler.Priority.IMMEDIATE); // 延时 2ms 后立即处理 
myHandler.sendEvent(event2, 2, EventHandler.Priority.IMMEDIATE); //6. 启动和停止 EventRunner,如果为托管模式,则不需要此步骤。 

runner.run(); 

//待执行操作 // ... 执行业务逻辑 
// 停止 EventRunner runner.stop(); } /
\*\* \* EventHandler 投递 Runnable 任务 \*/ 
private void initRunnable() { 
//2. 创建 EventRunner,以手动模式为例。 // create()的参数是 true 时,则为托管模式
 runner = EventRunner.create(false); 
if (runner == null) { return; } 
//3. 创建 EventHandler 子类的实例。 
myHandler = new MyEventHandler(runner); //2. 创建 Runnable 任务。 
Runnable task1 = new Runnable() {
 @Override public void run() {
 HiLog.info(label, "task1 running");
 } }; 
Runnable task2 = new Runnable() {
 @Override public void run() {
 HiLog.info(label, "task1 running"); 
  }
 }; //3. 投递 Runnable 任务,投递的优先级以 IMMEDIATE 为例,延时选择 0ms 和 2ms。 
myHandler.postTask(task1, 0, EventHandler.Priority.IMMEDIATE); // 延时 2ms 后立即执行 
myHandler.postTask(task2, 2, EventHandler.Priority.IMMEDIATE); 
//4. 启动和停止 EventRunner,如果是托管模式,则不需要此步骤。 runner.run(); 
//待执行操作 
// ... 执行业务逻辑
 // 停止 EventRunner runner.stop(); } /
\*\* \* 在新创建的线程里投递事件到原线程 \*/ 

private void initNewToOld() {
 //2. 创建 EventRunner,以手动模式为例。 
// create()的参数是 true 时,则为托管模式。 
EventRunner runner1 = EventRunner.create(false);
 // 需要对 EventRunner 的实例进行校验,不是任何线程都可以通过 create 创建,例如:当线程池已满时,不能再创建线程。
 if (runner1 == null) { return; } //3. 创建 EventHandler 子类的实例。
 MyEventHandler myHandler1 = new MyEventHandler(runner1); 
//4. 获取 InnerEvent 事件。 
// 获取事件实例,其属性 eventId, param, object 由开发者确定,代码中只是示例。
 int eventId1 = 0; long param = 0; 
Object object = (Object) EventRunner.current(); 
InnerEvent event1 = InnerEvent.get(eventId1, param, object);
 //5. 投递事件,在新线程上直接处理。 
// 将与当前线程绑定的 EventRunner 投递到与 runner1 创建的新线程中 myHandler.sendEvent(event1); 
//6. 启动和停止 EventRunner,如果是托管模式,则不需要此步骤。 runner.run(); 
//待执行操作
 // ... 执行业务逻辑
 // 停止
 EventRunner runner.stop();
   }
}

为了能让大家更好的学习鸿蒙 (OpenHarmony) 开发技术,这边特意整理了《鸿蒙 (OpenHarmony)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙 (OpenHarmony)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. ……

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门?:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1422588.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker的使用方式

一、Docker概念 Docker类似于一个轻量的虚拟机。 容器和镜像是Docker中最重要的两个概念&#xff0c;镜像可以保存为tar文件&#xff0c;Dockerfile是配置文件&#xff0c;仓库保存了很多第三方已经做好的镜像。 基本指令 查找镜像 docker search nginx 拉取nginx镜像 do…

文件归类整理,文件归类软件

数字化时代&#xff0c;我们经常面对的一个问题是&#xff1a;电脑中的文件越积越多&#xff0c;找起东西来如同大海捞针。这个时候&#xff0c;一款好的文件归类软件无疑能够帮你节省大量的时间和精力。那还等什么&#xff1f;快来试试这款【文件批量改名高手】软件吧&#xf…

MyISAM和InnoDB区别

MyISAM是括全文索引、压缩、空间函数等&#xff0c;但MyISAM不支持事MySQL的默认数据库引擎&#xff08;5.5版之前&#xff09;。虽然性能极佳&#xff0c;而且提供了大量的特性&#xff0c;包务和行级锁&#xff0c;而且最大的缺陷就是崩溃后无法安全恢复。不过&#xff0c;5.…

Leetcode—1828. 统计一个圆中点的数目【中等】

2024每日刷题&#xff08;一零五&#xff09; Leetcode—1828. 统计一个圆中点的数目 实现代码 class Solution { public:vector<int> countPoints(vector<vector<int>>& points, vector<vector<int>>& queries) {vector<int> a…

WIN11 - WSL(Windows Subsystem for Linux) 安装教程

前言 WSL&#xff0c;即Windows Subsystem for Linux&#xff0c;是一种在Windows操作系统上运行Linux二进制文件的兼容层。该层提供了Linux环境和GNU工具&#xff0c;可以在Windows系统上运行Linux应用程序。WSL使得开发人员可以在Windows系统上使用Linux工具和命令行界面&am…

GIS系统的类型

GIS&#xff08;地理信息系统&#xff09;系统是一种用于采集、存储、管理、分析和展示地理信息的软件系统。这些系统可以根据其功能和应用领域划分为不同的类型。以下是一些常见类型的GIS系统以及在其开发中的关键考虑因素&#xff0c;希望对大家有所帮助。北京木奇移动技术有…

万兆电口模块10GBase-T:提升网络性能的利器

随着数字化时代的到来&#xff0c;数据传输速度已经成为各行各业不可或缺的一项需求。而在数据中心和企业网络中&#xff0c;网络设备也正面临着越来越高的带宽需求。在满足这一需求的过程中&#xff0c;万兆电口模块10GBase-T成为了一种重要的解决方案。本文将围绕万兆电口模块…

ElementUI组件:Link 文字链接

ElementUI安装与使用指南 Link 文字链接 点击下载learnelementuispringboot项目源码 效果图 el-link.vue页面效果图 项目里el-link.vue文件代码 <script> export default {name: el_link }</script> <!--https://element.eleme.cn/#/zh-CN/component/link …

数据结构—栈实现前缀表达式的计算

前缀表达式计算 过程分析 中缀表达式&#xff1a;&#xff08;1 5&#xff09;*3 > 前缀表达式&#xff1a;*153 &#xff08;可参考这篇文章&#xff1a;中缀转前缀&#xff09; 第一步&#xff1a;从右至左扫描前缀表达式&#xff08;已存放在字符数组中&#xff09;&a…

百川终入海 ,一站式海量数据迁移工具 X2Doris 正式发布

在大数据分析领域&#xff0c;Apache Doris 作为广受认可的开源实时数据仓库&#xff0c;已经在越来越多行业用户的真实业务场景中得到广泛应用&#xff0c;成为许多企业数据分析基础设施的重要基座。尤其在过去一年多的时间里&#xff0c;越来越多企业选择基于 Apache Doris 进…

LeetCode.2808. 使循环数组所有元素相等的最少秒数

题目 题目链接 分析 我们最终形成的数组一定是当前数组nums 中的一个数字。 所以我们的想法就是枚举数组 nums 中的所有数字&#xff0c;取最小值。 题目告诉我们每一秒都可以向左右扩散一位&#xff0c;那么多个相同的 x 同时扩散&#xff0c;扩散完整个数组耗时就取决于两…

医院如何筛选安全合规的内外网文件交换系统?

医院内外网文件交换系统是专为医疗机构设计的&#xff0c;用于在内部网络&#xff08;内网&#xff09;和外部网络&#xff08;外网&#xff09;之间安全、高效地传输敏感医疗数据和文件的解决方案。这种系统对于保护患者隐私、遵守医疗数据保护法规以及确保医疗服务的连续性和…

NFTScan 与 Merlin Protocol 共同推出 BRC20 Indexer Oracle,于今日正式上线!

近日&#xff0c;NFT 数据基础设施 NFTScan 与 Merlin Protocol 进行战略合作&#xff0c;联合推出了比特币网络原生资产 Indexer Oracle 服务&#xff0c;现在该服务已在 NFTScan 开发者平台上线&#xff0c;任何开发者都可以注册使用&#xff01; Merlin Protocol 是一个专用…

python_蓝桥杯刷题记录_笔记_入门2

前言 现在正式进入蓝桥杯的刷题啦&#xff0c;用python来做算法题&#xff0c;因为我之前其实都是用C来做题的&#xff0c;但是今年的话我打算换python来试试&#xff0c;很明显因为也才这学期接触python 加上之前C做题也比较菜&#xff0c;所以我打算用python重新来做题&#…

Node.js Express 框架 2024版 笔记

1.0 操作命令 Node.js express 框架 https://www.expressjs.com.cn/ npm install -g express-generator expressexpress --pug --git // --pug 添加对 pug 模板引擎的支持 // --git 添加 .gitignore 代码仓库排除 //无法直接安装新版pug模板 npm i npm …

Linux进程间通信(IPC)机制之一:共享内存

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;Nonsense—Sabrina Carpenter 0:50━━━━━━️&#x1f49f;──────── 2:43 &#x1f504; ◀️ ⏸ ▶️ …

社区投稿|Desig质押聚合器功能,帮助用户选出更适合的质押策略

在Sui上&#xff0c;不同的质押协议提供收益各异的产品&#xff0c;因此用户面临着众多可以质押token的协议&#xff0c;眼花缭乱无从选择。Desig质押聚合器功能现已整合到Desig钱包扩展中&#xff0c;极大地简化了寻找质押策略的流程。事实上&#xff0c;其智能质押功能支持完…

泰迪智能科技生成式人工智能(AIGC)实验室解决方案

AIGC&#xff08;Artificial Intelligence Generated Content&#xff0c;生成式人工智能&#xff09;是一种新的人工智能技术&#xff0c;指的是利用人工智能技术来生成内容。这种技术可以自动生成文本、图像、音频和视频等多种类型的内容&#xff0c;而且内容的质量较高&…

Web3.0初探

Web3.0初探 一、互联网发展史二、什么是Web3.0&#xff1f;三、现在的发展方向&#xff08;衍生出来的产品&#xff09;&#xff1a;四、目前问题五、Web3.0与元宇宙 一、互联网发展史 Web3.0也就是第三代互联网。最新版本的Web3.0是以太坊的创始合伙人Gavin Wood在2014年提出…

CycleISP: Real Image Restoration via Improved Data Synthesis

Abstract 1、提出一个模拟 ISP 处理的模型&#xff08;模型是怎么构建的&#xff1f;&#xff09; 2、在 RAW、sRGB 域都能生成图像对&#xff0c;都能做去噪。&#xff08;它说在真是图像基准数据集上有 SOTA 效果&#xff0c;不会是 DND 吧&#xff09; 3、参数量是之前的RA…