OpenHarmony的线程间通信EventHandler

news2024/9/20 6:16:46

一、初识EventHandler

​ 在OpenHarmony的开发过程中,如果遇到处理下载、运算等较为耗时的操作时,会阻塞当前线程,但是实际操作中又不希望当前线程受到阻塞。比如:我们的app在界面上有一个下载文件的处理按钮,如果在按钮按下时,直接处理下载任务时,当前的界面就会阻塞,不允许操作界面上的任何控件。解决此阻塞的办法,一般的处理方式是通过新开一个线程或进程的来处理此耗时的操作,然后通过同步数据的方式来进行界面上关于下载进度的刷新。

​ OpenHarmony系统为此提供了统一的EventHandler机制来处理线程间通信。以下通过简单的实例来比较EventHandler机制的与一般的线程之间的方便性与易理解性。

1.1 使用EventHandler

1.1.1 EventHandler投递InnerEvent事件

  • 首先创建EventHandler的子类,在子类中重写实现ProcessEvent来处理事件。
// base\telephony\core_service\services\network_search\include\network_search_handler.h
class NetworkSearchHandler : public AppExecFwk::EventHandler {
public:
...
    void ProcessEvent(const AppExecFwk::InnerEvent::Pointer &event) override;
...
	void AutoTimeChange(const AppExecFwk::InnerEvent::Pointer &);
...
private:
	static const std::map<uint32_t, NsHandlerFunc> memberFuncMap_;
};
// base\telephony\core_service\services\network_search\src\network_search_handler.cpp
const std::map<uint32_t, NetworkSearchHandler::NsHandlerFunc> NetworkSearchHandler::memberFuncMap_ = {
...
	{ SettingEventCode::MSG_AUTO_TIME, &NetworkSearchHandler::AutoTimeChange },
...
};
...
void NetworkSearchHandler::ProcessEvent(const AppExecFwk::InnerEvent::Pointer &event)
{
    if (event == nullptr) {
        return;
    }
    auto msgType = event->GetInnerEventId();
    TELEPHONY_LOGD(
        "NetworkSearchHandler::ProcessEvent received event slotId:%{public}d msgType:%{public}d", slotId_, msgType);
    auto itFunc = memberFuncMap_.find(static_cast<RadioEvent>(msgType));
    if (itFunc != memberFuncMap_.end()) {
        auto memberFunc = itFunc->second;
        if (memberFunc != nullptr) {
            (this->*memberFunc)(event);
        }
    }
}

在ProcessEvent中根据事件类型Id映射对应的处理接口。

  • 创建EventRunner事件循环器,EventRunner创建新线程,从EventQueue队列中获取InnerEvent事件,分发到EventHandler的子类的ProcessEvent进行处理。
  • 关联EventRunner至EventHandler的子类NetworkSearchHandler
// base\telephony\core_service\services\network_search\src\network_search_manager.cpp
bool NetworkSearchManager::InitPointer(std::shared_ptr<NetworkSearchManagerInner> &inner, int32_t slotId)
{
    if (inner == nullptr) {
        TELEPHONY_LOGE("NetworkSearchManager::InitPointer failed . inner is null");
        return false;
    }
    std::string name = "NetworkSearchManager_";
    name.append(std::to_string(slotId));
    // 创建EventRunner事件循环器
    inner->eventLoop_ = AppExecFwk::EventRunner::Create(name.c_str());
    if (inner->eventLoop_.get() == nullptr) {
        TELEPHONY_LOGE("NetworkSearchManager failed to create EventRunner slotId:%{public}d", slotId);
        return false;
    }
...
    // 关联EventRunner至EventHandler的子类NetworkSearchHandler
    inner->networkSearchHandler_ = std::make_shared<NetworkSearchHandler>(
        inner->eventLoop_, shared_from_this(), telRilManager_, simManager_, slotId);
    if (inner->networkSearchHandler_ == nullptr) {
        TELEPHONY_LOGE("failed to create new NetworkSearchHandler slotId:%{public}d", slotId);
        return false;
    }
...
    return true;
}
  • 启动EventRunner: eventLoop_->Run();
// base\telephony\core_service\services\network_search\include\network_search_manager.h
bool Init()
{
...
    if (eventLoop_ != nullptr) {
        eventLoop_->Run();
    }
...
    return true;
}
  • InnerEvent事件投递: networkSearchHandler_->SendEvent(event);
// base\telephony\core_service\services\network_search\src\setting_utils.cpp
void AutoTimeObserver::OnChange()
{
    std::shared_ptr<SettingUtils> settingHelper = SettingUtils::GetInstance();
    if (settingHelper == nullptr) {
        TELEPHONY_LOGI("settingHelper is null");
        return;
    }

    TELEPHONY_LOGI("AutoTimeObserver::OnChange");
    if (networkSearchHandler_ != nullptr) {
        InnerEvent::Pointer event = InnerEvent::Get(SettingEventCode::MSG_AUTO_TIME);
        networkSearchHandler_->SendEvent(event);
    }
}

根据投递的事件SettingEventCode::MSG_AUTO_TIME,在NetworkSearchHandler的memberFuncMap_中找到对应的实现AutoTimeChange

// base\telephony\core_service\services\network_search\src\network_search_handler.cpp
void NetworkSearchHandler::AutoTimeChange(const AppExecFwk::InnerEvent::Pointer &)
{
    TELEPHONY_LOGD("NetworkSearchHandler::AutoTimeChange");
    if (nitzUpdate_ != nullptr) {
        nitzUpdate_->AutoTimeChange();
    }
}

至此通过InnerEvent事件完成了异步的事件处理。

1.1.2 EventHandler投递Runnable任务

此种使用方式基本同上.

  • 首先创建EventHandler的子类,在子类中重写实现ProcessEvent来处理事件。
  • 创建回调函数
  • 投递Runnable任务
// base\account\os_account\services\accountmgr\src\appaccount\app_account_subscribe_manager.cpp
Callback callback = std::bind(&AppAccountSubscribeManager::OnAccountsChanged, this, eventRecordPtr);
return handler_->PostTask(callback);

二、原理概述

​ EventHandler是一种用户在当前线程上投递InnerEvent事件或者Runnable任务到异步线程上处理的机制。每一个EventHandler和指定的EventRunner所创建的线程绑定,并且该线程内部有一个事件队列。

​ EeventHandler可以投递指定的InnerEvent事件或Runnable任务到这个队列。EventRunner从事件队列里循环地取出事件,如果取出的事件是InnerEvent事件,将在EventRunner所在线程执行ProcessEvent回调。如果取出的是Runnable任务,将在EventRunner所在线程执行Runnable的run回调。

​ 一般来说,EventHandler有两个主要作用:

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

三、运行机制

在这里插入图片描述

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

  • EventHandler投递具体的InnerEvent事件或者Runnable任务到EventRunner所创建的线程的事件队列。
  • EventHandler循环从事件队列中获取InnerEvent事件或者Runnable任务
  • 处理事件或任务,如果取出的事件是InnerEvent事件,将在EventRunner所在线程执行ProcessEvent回调。如果取出的是Runnable任务,将在EventRunner所在线程执行Runnable的run回调。

四、约束条件

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

​ 一个EventHandler只能同时与一个EventRunner绑定,一个EventRunner可以同时绑定多个EventHandler.

五、其它

5.1 关于sendEvent方法

sendEvent(InnerEvent event, long delayTime, EventHandler.Priority priority)
  • delayTime:Indicates the time period after which the event will be processed, in milliseconds. The value must be greater than 0. Otherwise, the event will not be delayed.

  • priority:Indicates the event priority.四类:HIGH、IDLE、IMMEDIATE、LOW

5.2 关于sendEvent传参的示例

// base\telephony\call_manager\services\call\include\call_request_handler.h
struct AnswerCallPara {
    int32_t callId;
    int32_t videoState;
};

// base\telephony\call_manager\services\call\src\call_request_handler.cpp
CallRequestHandler::CallRequestHandler(const std::shared_ptr<AppExecFwk::EventRunner> &runner)
    : AppExecFwk::EventHandler(runner), callRequestProcessPtr_(nullptr)
{
    memberFuncMap_[CallRequestHandlerService::HANDLER_DIAL_CALL_REQUEST] = &CallRequestHandler::DialCallEvent;
    memberFuncMap_[CallRequestHandlerService::HANDLER_ANSWER_CALL_REQUEST] = &CallRequestHandler::AcceptCallEvent;
...
}
...
void CallRequestHandler::AcceptCallEvent(const AppExecFwk::InnerEvent::Pointer &event)
{
    if (event == nullptr) {
        TELEPHONY_LOGE("CallRequestHandler::ProcessEvent parameter error");
        return;
    }
    auto object = event->GetUniqueObject<AnswerCallPara>();
    if (object == nullptr) {
        TELEPHONY_LOGE("object is nullptr!");
        return;
    }
    AnswerCallPara acceptPara = *object;
    if (callRequestProcessPtr_ == nullptr) {
        TELEPHONY_LOGE("callRequestProcessPtr_ is nullptr");
        return;
    }
    callRequestProcessPtr_->AnswerRequest(acceptPara.callId, acceptPara.videoState);
}
...
int32_t CallRequestHandlerService::AnswerCall(int32_t callId, int32_t videoState)
{
    if (handler_.get() == nullptr) {
        TELEPHONY_LOGE("handler_ is nullptr");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }
    std::unique_ptr<AnswerCallPara> para = std::make_unique<AnswerCallPara>();
    if (para.get() == nullptr) {
        TELEPHONY_LOGE("make_unique AnswerCallPara failed!");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }
    para->callId = callId;
    para->videoState = videoState;
    if (!handler_->SendEvent(HANDLER_ANSWER_CALL_REQUEST, std::move(para))) {
        TELEPHONY_LOGE("send accept event failed!");
        CallManagerHisysevent::WriteAnswerCallFaultEvent(INVALID_PARAMETER, callId, videoState,
            CALL_ERR_SYSTEM_EVENT_HANDLE_FAILURE, "send HANDLER_ANSWER_CALL_REQUEST event failed");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }
    return TELEPHONY_SUCCESS;
}

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

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

相关文章

计算机网络基础 第三章练习题

计算机网络基础 第三章练习题 现在大量的计算机是通过诸如以太网这样的局域网连入广域网的&#xff0c;而局域网与广城网的互联是通过( A)实现的。 A. 路由器B. 资源子网C. 桥接器D. 中继器 下列不属于数据链路层功能的是(B )。 A. 帧定界功能B. 电路管理功能C. 差错控制功能D…

HCIP——交换

交换 园区网架构 交换机实现了一下功能 无限的传输距离——识别&#xff0c;重写电信号&#xff08;帧&#xff09;保证信息完整彻底解决了冲突二层单播——MAC地址表提高端口密度 MAC 单播地址&#xff1a;MAC地址第一个字节第8位为0 组播地址&#xff1a;MAC地址第一个字…

Camera | 8.让rk3568支持前后置摄像头

一、目标 本文主要目标是&#xff0c;支持前置摄像头0v5648、后置摄像头ov13850&#xff0c;以及移植过程遇到的一些小问题的解决。 1. 摄像头连接图 参考上图&#xff0c;摄像头详细信息如下&#xff1a; 2个摄像头均连接在I2C通道42个摄像头共用同一个MIPI数据通道2个摄像…

C++——探究引用

文章目录 概述引用的概念引用特性引用的作用**引用做参数****引用作为函数返回值** 常引用引用的底层实现总结一下引用和指针的不同点 概述 本篇博客将讲述c相对于c新增的一个重要的内容——引用&#xff0c;深入研究其语法细节以及其需要注意的一些要点。 引用的概念 竟然要学…

Baumer工业相机堡盟工业相机如何联合BGAPISDK和佳能EF变焦镜头实现相机的自动变焦(C++)

Baumer工业相机堡盟工业相机如何联合BGAPISDK和佳能EF变焦镜头实现相机的自动变焦&#xff08;C&#xff09; Baumer工业相机Baumer工业相机BGAPISDK中控制变焦镜头的技术背景代码案例分享第一步&#xff1a;开启相机自动调焦功能模块第二步&#xff1a;控制自动变焦镜头电机的…

Java+SSM+Mysql在线图书超市

课题背景及意义 随着现代网络技术发展&#xff0c;对于在线图书超市的设计现在正处于发展的阶段&#xff0c;所以对的要求也是比较严格的&#xff0c;要从系统的功能和用户实际需求来进行对系统制定开发的发展方式&#xff0c;依靠网络技术的的快速发展和现代通讯技术的结合为…

C++11: lambda、包装器、模板参数包和线程库

lambda C98中的一个例子 在C98中&#xff0c;如果想要对一个数据集合中的元素进行排序&#xff0c;可以使用std::sort方法。 int main() {int array[] { 4,1,8,5,3,7,0,9,2,6 };// 默认按照小于比较&#xff0c;排出来结果是升序std::sort(array, array sizeof(array) / s…

socket.io 解决浏览器兼容性(WebSocket)

在上一篇讲了 npm 上最流行的 WebSocket 库之一的 ws 库&#xff0c;那么本篇就来讲另外一个&#xff0c;就是 socket.io 库&#xff0c;socket.io 其实是一个兼容方案&#xff0c;当浏览器不支持 H5 的情况下就不能够使用上一篇内容讲的 WebSocket &#xff0c;只能采用其他的…

【数据统计】— 极大似然估计 MLE、最大后验估计 MAP、贝叶斯估计

【数据统计】— 极大似然估计 MLE、最大后验估计 MAP、贝叶斯估计 极大似然估计、最大后验概率估计(MAP)&#xff0c;贝叶斯估计极大似然估计(Maximum Likelihood Estimate&#xff0c;MLE)MLE目标例子: 扔硬币极大似然估计—高斯分布的参数 矩估计 vs LSE vs MLE贝叶斯公式&am…

JavaScript函数基础

●我们代码里面所说的函数和我们上学的时候学习的什么三角函数、二次函数之类的不是一个东西 函数的概念 ●对于 js 来说&#xff0c;函数就是把任意一段代码放在一个 盒子 里面 ●在我想要让这段代码执行的时候&#xff0c;直接执行这个 盒子 里面的代码就行 ●先看一段代码 …

[golang gin框架] 25.Gin 商城项目-配置清除缓存以及前台列表页面数据渲染公共数据

配置清除缓存 当进入前台首页时,会缓存对应的商品相关数据,这时,如果后台修改了商品的相关数据,缓存中的对应数据并没有随之发生改变,这时就需要需改对应的缓存数据,这里有两种方法: 方法一 在管理后台操作直接清除缓存中的所有数据,当再次访问前台首页时,就会先从数据库中获取…

巧用千寻位置GNSS软件|直线放样有技巧

日常测量作业中&#xff0c;直线放样是对设计好的直线进行放样&#xff0c;其中包括直线的里程&#xff0c;左右偏距及设计直线范围内的高程控制。本文将介绍如何运用千寻位置GNSS软件完成日常的直线放样。 点击【测量】->【直线放样】->【直线库】&#xff0c;选择一条直…

Python数组仿射变换

文章目录 仿射变换坐标变换的逻辑scipy实现 仿射变换 前面提到的平移、旋转以及缩放&#xff0c;都可以通过一个变换矩阵来实现&#xff0c;以二维空间中的变换矩阵为例&#xff0c;记点的坐标向量为 ( x , y , 1 ) (x,y,1) (x,y,1)&#xff0c;则平移矩阵可表示为 [ 1 0 T x …

数据结构和算法学习记录——二叉搜索树的插入操作、删除操作

目录 二叉搜索树的插入 思路图解 代码实现 要点 例题 二叉搜索树的删除 情况一 情况二 情况三 右子树的最小元素 左子树的最大元素 代码实现 二叉搜索树的插入 要进行二叉搜索树的插入&#xff0c; 关键点在于要找到元素应该插入到哪个位置&#xff0c;可以采…

Echarts数据可视化(下)

四、数据可视化项目实战 后台搭建 结合Vue开发图表组件 WebSocket实现数据推送 主题切换/页面合并/全屏切换 4.1 Koa2 学习介绍&#xff1a;koa2 - 简书 官网&#xff1a;Koa (koajs) -- 基于 Node.js 平台的下一代 web 开发框架 | Koajs 中文文档 4.1.1 Koa2 的介绍 基…

springboot+vue校园新闻网站(源码+说明文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的校园新闻网站。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌&a…

【数据结构】树及二叉树的概念

&#x1f61b;作者&#xff1a;日出等日落 &#x1f4d8; 专栏&#xff1a;数据结构 一次失败&#xff0c;只是证明我们成功的决心还够坚强。 ——博 维 目录 &#x1f384;树概念及结构: ✔树的概念: ✔树的相关概念 :​编辑 ✔树的…

【C++】-一文让你半只脚进入C++,还不进来看看??

作者&#xff1a;小树苗渴望变成参天大树 作者宣言&#xff1a;认真写好每一篇博客 作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; C入门 前言一、C关键字二、namespace和using关键字2.1namespace和using的使用2.2names…

WebStorm前端启动JetLinks 物联网基础平台(2.x)

目录 一、环境准备 二、下载源码 三、安装依赖 ​四、修改配置 五、启动项目 六、访问项目 一、环境准备 1.降级node版本为12.22.0 使用node版本管理器gnvm_苍穹之跃的博客-CSDN博客以管理员身份打开cmd&#xff0c;cd到node安装目录。https://blog.csdn.net/wenxingch…

联发科 2024届 IC实习笔试分析

说明 记录一下 4月19日晚&#xff0c;发哥的一场笔试。分享给需要的 IC 人。 岗位&#xff1a;数字IC设计验证&#xff08;安徽 合肥&#xff09; 转载要经本人同意&#xff01; 我的见解不一定都是准确的&#xff0c;欢迎评论区交流指正~~ 1、&#xff08;20分&#xff0…