OpenHarmony's inter-thread communication EventHandler

1. First introduction to EventHandler

​ During the development process of OpenHarmony, if time-consuming operations such as downloading and calculation are encountered, the current thread will be blocked, but in actual operations, we do not want the current thread to be blocked. For example: Our app has a download file processing button on the interface. If the download task is directly processed when the button is pressed, the current interface will be blocked and no controls on the interface will be allowed to be operated. The general way to solve this blocking is to open a new thread or process to handle this time-consuming operation, and then refresh the download progress on the interface by synchronizing data.

​ The OpenHarmony system provides a unified EventHandler mechanism to handle inter-thread communication. The following is a simple example to compare the convenience and understandability between the EventHandler mechanism and general threads.

1.1 Using EventHandler

1.1.1 EventHandler delivers InnerEvent event

  • First create a subclass of EventHandler, and rewrite the implementation of ProcessEvent in the subclass to handle events.
// 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);
        }
    }
}

In ProcessEvent, the corresponding processing interface is mapped according to the event type Id.

  • Create an EventRunner event looper, EventRunner creates a new thread, obtains the InnerEvent event from the EventQueue queue, and distributes it to the ProcessEvent of the subclass of EventHandler for processing.
  • Associate EventRunner to NetworkSearchHandler, a subclass of EventHandler
// 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;
}
  • Start 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 event delivery: 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);
    }
}

According to the delivered event SettingEventCode::MSG_AUTO_TIME, find the corresponding implementation AutoTimeChange in memberFuncMap_ of NetworkSearchHandler

// 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();
    }
}

At this point, asynchronous event processing is completed through the InnerEvent event.

1.1.2 EventHandler delivers Runnable tasks

This usage is basically the same as above.

  • First create a subclass of EventHandler, and rewrite the implementation of ProcessEvent in the subclass to handle events.
  • Create callback function
  • Deliver Runnable tasks
// 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);

2. Principle Overview

EventHandler is a mechanism for users to deliver InnerEvent events or Runnable tasks on the current thread to asynchronous threads for processing. Each EventHandler is bound to the thread created by the specified EventRunner, and there is an event queue inside the thread.

​ EeventHandler can deliver the specified InnerEvent event or Runnable task to this queue. EventRunner cyclically retrieves events from the event queue. If the event retrieved is an InnerEvent event, the ProcessEvent callback will be executed in the thread where EventRunner is located. If a Runnable task is taken out, the Runnable's run callback will be executed on the thread where the EventRunner is located.

Generally speaking, EventHandler has two main functions:

  • Distribute and process InnerEvent events or Runnable tasks among different threads.
  • Delay processing of InnerEvent events or Runnable tasks.

3. Operating mechanism

Insert image description here

The main process of using EventHandler to implement inter-thread communication

  • EventHandler delivers specific InnerEvent events or Runnable tasks to the event queue of the thread created by EventRunner.
  • EventHandler loop obtains InnerEvent events or Runnable tasks from the event queue
  • Process events or tasks. If the event taken out is an InnerEvent event, the ProcessEvent callback will be executed on the thread where the EventRunner is located. If a Runnable task is taken out, the Runnable's run callback will be executed on the thread where the EventRunner is located.

4. Constraints

​ When communicating between threads, EventHandler can only be bound to the thread created by EventRunner. When creating EventRunner, it is necessary to determine whether the creation is successful. Only when it is ensured that the obtained EventRunner instance is not empty, can EventHandler be bound to EventRunner.

​ An EventHandler can only be bound to one EventRunner at the same time, and an EventRunner can be bound to multiple EventHandlers at the same time.

5. Others

5.1 About sendEvent method

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 Examples of parameter passing in 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;
}

Guess you like

Origin blog.csdn.net/procedurecode/article/details/130275093