线程通信概述
作者:韩茹在开发过程中,我们经常需要在当前线程中处理下载任务等较为耗时的操作,但是又不希望当前的线程受到阻塞。此时,就可以使用EventHandler机制。EventHandler是HarmonyOS用于处理线程间通信的一种机制,可以通过EventRunner创建新线程,将耗时的操作放到新线程上执行。这样既不阻塞原来的线程,任务又可以得到合理的处理。比如:主线程使用EventHandler创建子线程,子线程做耗时的下载图片操作,下载完成后,子线程通过EventHandler通知主线程,主线程再更新UI。
公司:程序咖(北京)科技有限公司
鸿蒙巴士专栏作家
一、主线程更新UI
所有的UI操作都应该在主线程进行设置。
我们可以尝试一下在子线程中设置UI,看会有什么结果。首先在ability_main.xml中添加两个按钮和一个Text:
在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实现线程间通信的主要流程:
- 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。
推荐阅读
- Docker应用:容器间通信与Mariadb数据库主从复制
- 深入理解Go之generate
- Linux下面如何查看tomcat已经使用多少线程
- 多线程NSOperation
- Unity和Android通信系列文章2——扩展UnityPlayerActivity
- Android|Android BLE蓝牙连接异常处理
- spring|spring boot中设置异步请求默认使用的线程池
- KubeDL HostNetwork(加速分布式训练通信效率)
- Flutter自定义view|Flutter自定义view —— 闯关进度条
- Android中非UI主线程能不能操作UI()