作者:韩茹

公司:程序咖(北京)科技有限公司

鸿蒙巴士专栏作家

在开发过程中,咱们常常须要在以后线程中解决下载工作等较为耗时的操作,然而又不心愿以后的线程受到阻塞。此时,就能够应用EventHandler机制。EventHandler是HarmonyOS用于解决线程间通信的一种机制,能够通过EventRunner创立新线程,将耗时的操作放到新线程上执行。这样既不阻塞原来的线程,工作又能够失去正当的解决。比方:主线程应用EventHandler创立子线程,子线程做耗时的下载图片操作,下载实现后,子线程通过EventHandler告诉主线程,主线程再更新UI。

一、主线程更新UI

所有的UI操作都应该在主线程进行设置。

咱们能够尝试一下在子线程中设置UI,看会有什么后果。首先在ability_main.xml中增加两个按钮和一个Text:

<?xml version="1.0" encoding="utf-8"?><DirectionalLayout    xmlns:ohos="http://schemas.huawei.com/res/ohos"    ohos:height="match_parent"    ohos:width="match_parent"    ohos:padding="20vp"    ohos:orientation="vertical">    <Button        ohos:id="$+id:btn1"        ohos:height="match_content"        ohos:width="match_content"        ohos:text="设置文本"        ohos:background_element="#eeeeee"        ohos:padding="20vp"        ohos:text_size="20fp"        ohos:margin="10vp"        />    <Button        ohos:id="$+id:btn2"        ohos:height="match_content"        ohos:width="match_content"        ohos:text="启动子线程设置文本"        ohos:background_element="#eeeeee"        ohos:padding="20vp"        ohos:text_size="20fp"        ohos:margin="10vp"        />    <Text        ohos:id="$+id:text1"        ohos:height="200vp"        ohos:width="300vp"        ohos:text="文本"        ohos:background_element="#eeeeee"        ohos:padding="20vp"        ohos:text_size="20fp"        ohos:text_alignment="center"        ohos:margin="10vp"        /></DirectionalLayout>

在MainAbilitySlice中,解决这两个按钮的点击事件:

package com.example.hanrueventhandler.slice;import com.example.hanrueventhandler.ResourceTable;import ohos.aafwk.ability.AbilitySlice;import ohos.aafwk.content.Intent;import ohos.agp.components.Button;import ohos.agp.components.Component;import ohos.agp.components.Text;import ohos.hiviewdfx.HiLog;import ohos.hiviewdfx.HiLogLabel;public class MainAbilitySlice extends AbilitySlice {    // 定义日志标签    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "MY_TAG");    @Override    public void onStart(Intent intent) {        super.onStart(intent);        super.setUIContent(ResourceTable.Layout_ability_main);        //打印日志        // I 00201/MY_TAG: ===线程ID号:1,线程名称:main        HiLog.info(LABEL, "===线程ID号:" + Thread.currentThread().getId() + ",线程名称:" + Thread.currentThread().getName());        //UI线程中更改textview的数据。。        Button btn1 = (Button) findComponentById(ResourceTable.Id_btn1);        Button btn2 = (Button) findComponentById(ResourceTable.Id_btn2);        Text text = (Text) findComponentById(ResourceTable.Id_text1);        btn1.setClickedListener(component -> {            // 先睡5s            try {                HiLog.info(LABEL,"UI线程睡眠5s");                Thread.sleep(5*1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            text.setText("面朝大海哈");        });        btn2.setClickedListener(component -> {            // 换到子线程试试 ,程序间接挂掉了。所以子线程不能操作UI                new Thread(){                    @Override                    public void run() {                        HiLog.info(LABEL, "=======线程ID号:" + Thread.currentThread().getId() + ",线程名称:" + Thread.currentThread().getName());                        text.setText("春暖花开"); // java.lang.IllegalStateException: Attempt to update UI in non-UI thread.                    }                }.start();        });    }}

程序运行起来后,咱们就能看到打印的日志,UI线程就是main线程:

而后咱们点击第一个按钮:

点击按钮5s后,文本内容被设置了。

接下来咱们再来点击第二个按钮,能够打印出子线程:

而后程序就崩掉了:

而后咱们能够看到管制台上的错误信息:

由此咱们晓得了,在HarmonyOS中,子线程不能操作UI。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实现线程间通信的次要流程:

  1. EventHandler投递具体的InnerEvent事件或者Runnable工作到EventRunner所创立的线程的事件队列。
  2. EventRunner循环从事件队列中获取InnerEvent事件或者Runnable工作。
  3. 处理事件或工作:

    • 如果EventRunner取出的事件为InnerEvent事件,则触发EventHandler的回调办法并触发EventHandler的解决办法,在新线程上解决该事件。
    • 如果EventRunner取出的事件为Runnable工作,则EventRunner间接在新线程上解决Runnable工作。

四、束缚限度

  • 在进行线程间通信的时候,EventHandler只能和EventRunner所创立的线程进行绑定,EventRunner创立时须要判断是否创立胜利,只有确保获取的EventRunner实例非空时,才能够应用EventHandler绑定EventRunner。
  • 一个EventHandler只能同时与一个EventRunner绑定,一个EventRunner能够同时绑定多个EventHandler。