首页 > 其他分享 >OpenHarmony的线程间通信EventHandler

OpenHarmony的线程间通信EventHandler

时间:2023-04-23 14:01:51浏览次数:39  
标签:EventHandler OpenHarmony InnerEvent nullptr EventRunner 间通信 线程 event

一、初识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任务。

三、运行机制

eh1.png 使用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;
}

标签:EventHandler,OpenHarmony,InnerEvent,nullptr,EventRunner,间通信,线程,event
From: https://blog.51cto.com/u_15304012/6217519

相关文章

  • MFC-CreateProcess创建一个新的进程和它的主线程并执行外部程序
     CONSTTCHARpath[]=_T("C:\\Users\\Administrator\\Desktop\\test.exe");PROCESS_INFORMATIONinfo;STARTUPINFOsi={sizeof(si)};BOOLb=CreateProcess(path,NULL,NULL,NULL,FALSE,NULL,NULL,NULL,&si,&info);/......
  • Android多线程通信-handler机制
    AndroidStudio主线程不允许耗时操作,最后通过多线程实现了AndroidStudio远程连接数据库但是主线程可以分线程传值,分线程却不可以直接向主线程传值AndroidStudio通过handler和massage解决了这个问题 话不多说,看代码——privateHandlerhandler=newHandler(){@Overridep......
  • Python之多进程与多线程
     1.1线程frommultiprocessingimportProcessdefshow(name):print("Processnameis"+name)if__name__=="__main__":proc=Process(target=show,args=('subprocess',))proc.start()proc.join()......
  • 线程的地址空间问题(转)
    原文:https://blog.csdn.net/qq_41148436/article/details/121684129Linux系统把所有线程都当做进程来实现,线程作为轻量级进程(LWP)。线程仅仅被视为一个与其他进程共享某些资源的进程,而是否共享地址空间几乎是进程和Linux中所谓线程的唯一区别。线程创建的时候,加上了CLONE_VM......
  • 线程池使用小结
    在Java中,Executors是一个线程池的工厂类,它可以创建不同类型的线程池。下面是几种常见的Executors线程池,以及它们的使用区别:FixedThreadPool:这种类型的线程池有一个固定的线程数量,一旦线程池中的全部线程都在处理任务,那么后续提交的任务将会等待。如果应用程序需要限制线程数量,......
  • python--多线程:锁 、全局锁、Queue队列以及线程池
    关于如何加锁,获取钥匙,释放锁:lock=threading.Lock():生成锁对象,全局唯一;lock.acquire():获取锁。未获取到会阻塞程序,直到获取到锁才会往下执行;lock.release():释放锁,归回后,其他人也可以调用;【注意事项】:lock.acquire()和lock.release()必须成对出现,否则就有可能造成......
  • Openharmony上如何运行HelloWorld
    Openharmony的第一个应用--Helloworld​ 大家都明白的HelloWorld的C++代码//FirstApp.cpp#include<iostream>intmain(intargc,char*argv[]){std::cout<<"HelloOpenharmony!"<<std::endl;return0;}问题来了,接下来如何将FirstApp.cpp加入Openh......
  • Openharmony应用NAPI详解--基础篇
    NAPI是什么?​ 简单点理解就是在Openharmony里,实现上层js或ets应用与底层C/C++之间交互的框架。​ Openharmony里的官方解释:NAPI(NativeAPI)组件是一套对外接口基于Node.jsN-API规范开发的原生模块扩展开发框架。还有NAPI适合封装IO、CPU密集型、OS底层等能力并对外暴露JS接口,通过......
  • Openharmony应用NAPI详解--进阶篇1
    NAPI面向C++的异步接口3.C++实现NAPI异步接口需要做到三步同步返回结果给js/ets调用者另起线程完成异步操作通过回调(callback)或Promise将异步操作结果返回4.异步接口//foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp...DECLARE......
  • Openharmony应用NAPI详解--进阶篇2
    NAPI面向C++的异步接口(promise)promise方式的处理方式承接上文,与callback方式不同的是,promise对象由C++侧创建以返回值的方式传递回js/ets侧,promise对象存储异步执行的结果。//foundation/filemanagement/file_api/interfaces/kits/js/src/common/napi/n_async/n_async_work_p......