> 文档中心 > OpenHarmony的线程间通信EventHandler

OpenHarmony的线程间通信EventHandler


一、初识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.hclass NetworkSearchHandler : public AppExecFwk::EventHandler {public:...    void ProcessEvent(const AppExecFwk::InnerEvent::Pointer &event) override;...void AutoTimeChange(const AppExecFwk::InnerEvent::Pointer &);...private:static const std::map memberFuncMap_;};
// base\telephony\core_service\services\network_search\src\network_search_handler.cppconst std::map 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(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.cppbool NetworkSearchManager::InitPointer(std::shared_ptr &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( 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.hbool Init(){...    if (eventLoop_ != nullptr) { eventLoop_->Run();    }...    return true;}
  • InnerEvent事件投递: networkSearchHandler_->SendEvent(event);
// base\telephony\core_service\services\network_search\src\setting_utils.cppvoid AutoTimeObserver::OnChange(){    std::shared_ptr 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.cppvoid 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.cppCallback 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.hstruct AnswerCallPara {    int32_t callId;    int32_t videoState;};// base\telephony\call_manager\services\call\src\call_request_handler.cppCallRequestHandler::CallRequestHandler(const std::shared_ptr &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();    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 para = std::make_unique();    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;}