查看: 106|回复: 1

【SUBJECT技术】OpenHarmony 在一台设备从本端监听远端消亡的实现方法

[复制链接]

2

主题

1

回帖

16

积分

新手上路

积分
16
发表于 2025-3-30 12:10:37 | 显示全部楼层 |阅读模式
背景

双方模块进行交互,如果一方挂掉了,另一方还如痴如醉地一直为它工作,显然是有悖常理的。一旦对方不幸结束了,我们如何感知到并且正常开展后续工作?本文综合了Sensors,msdp模块进行了详实的介绍,希望对小伙们的此类工作有所帮助。
以下实例全部基于"client,proxy→stub,service"架构。
目录

    1.原理介绍2.几种情况的重点说明
      2.1 例1:服务侧监听客户侧消亡2.2 例2:客户端监听服务端消亡2.3 例3:服务端监听客户端消亡2.4 例4:服务端监听底层HDI侧消亡
    3. 传感器服务端监听不同客户端消亡的处理实例4.小结
一、原理介绍

1. HarmonyOS远端状态订阅开发实例

IPC/RPC提供对远端Stub对象状态的订阅机制, 在远端Stub对象消亡时,可触发消亡通知告诉本地Proxy对象。这种状态通知订阅需要调用特定接口完成,当不再需要订阅时也需要调用特定接口取消。使用这种订阅机制的用户,需要实现消亡通知接口DeathRecipient并实现onRemoteDied方法清理资源。该方法会在远端Stub对象所在进程消亡时被回调。值得注意的是,调用这些接口有一定的顺序。首先,需要Proxy订阅Stub消亡通知,若在订阅期间Stub状态正常,则在不再需要时取消订阅;若在订阅期间Stub所在进程退出,则会自动触发Proxy自定义的后续操作。
2. 使用场景

这种订阅机制适用于本地Proxy对象需要感知远端Stub对象所在进程消亡。当Proxy感知到Stub端消亡后,可适当清理本地资源。此外,RPC目前不提供匿名Stub对象的消亡通知,即只有向SAMgr注册过的服务才能被订阅消亡通知,IPC则支持匿名对象的消亡通知。
3. Native侧接口

接口名返回值类型功能描述
AddDeathRecipient(const sptr &recipient);bool订阅远端Stub对象状态。
RemoveDeathRecipient(const sptr &recipient);bool取消订阅远端Stub对象状态。
OnRemoteDied(const wptr &object);void当远端Stub对象消亡时回调。
4. 调用序列图



二、几种情况的重点说明

为了方便阅读和理解,名称有所修改,无关的代码被删除。远端(被监听)是消亡的一侧,本端是处理消亡的一侧。本端既可以在客户侧,也可以在服务侧。一般本端在客户侧是用来监听服务侧消亡情况,在服务侧是用来监听客户侧消亡的情况,或者底层提供服务侧的消亡情况。
只要出现 OnRemoteDied() 的地方就是本端,它是用来处理消亡的地方。
例1:服务侧监听客户侧消亡

在服务侧创建消亡信息接收者对象,添加、删除监听,以及消亡响应处理忽略,着重看一下客户侧如何将被监听的对象一路传递到服务侧的。
在远端要做的事情

步骤1. 定义被监听者类 ClientStubObject
  1. class IRemoteClientObject : public IRemoteBroker {
  2. public:
  3.     DECLARE_INTERFACE_DESCRIPTOR(u"ohos.xxx.IRemoteClientObject");// 必须存在,不然找不到该对象
  4. };
  5. class ClientStubObject :  public IRemoteStub {
  6. public:
  7.     explicit ClientStubObject(napi_env env) : env_(env) {}
  8.     virtual ~ClientStubObject() {};
  9.     int32_t OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override;
  10. };
复制代码
步骤2. 创建被监听者类对象
  1.     sptr object = new (std::nothrow) ClientStubObject(env);
复制代码
步骤3. 把 object 一路传递给服务侧
  1. void DeviceStatusSrvProxy::Subscribe(sptr object)
  2. {
  3.     sptr remote = Remote();
  4.     MessageParcel data;
  5.     WRITEREMOTEOBJECT(data, object->AsObject());
  6.     MessageParcel reply;
  7.     MessageOption option;
  8.     int32_t ret = remote->SendRequest(static_cast(DeviceInterfaceCode::DEVICESTATUS_SUBSCRIBE),
  9.         data, reply, option);
  10. }
  11. int32_t DeviceStatusSrvStub::SubscribeStub(MessageParcel &data, MessageParcel &reply)
  12. {
  13.     sptr obj = data.ReadRemoteObject();
  14.     sptr object = iface_cast(obj);
  15.     return RET_OK;
  16. }
复制代码
例2:客户端监听服务端消亡

此种情况处理较为简单, 全部代码在客户侧(本端)实现。
步骤1. 定义消亡信息接收者类
  1.     class DeviceStatusDeathRecipient : public IRemoteObject::DeathRecipient {
  2.     public:
  3.         DeviceStatusDeathRecipient() = default;
  4.         ~DeviceStatusDeathRecipient() = default;
  5.         void OnRemoteDied(const wptr &remote);
  6.     private:
  7.         DISALLOW_COPY_AND_MOVE(DeviceStatusDeathRecipient);
  8.     };
复制代码
步骤2. 消亡信息接收到的处理
  1. void DeviceStatusClient::DeviceStatusDeathRecipient::OnRemoteDied(const wptr &remote)
  2. {
  3.     DeviceStatusClient::GetInstance().ResetProxy(remote);
  4.     LOGD("Recv death notice");
  5. }
复制代码
步骤3. 客户端定义消亡信息接收者对象
  1. class DeviceStatusClient final : public DelayedRefSingleton {
  2.     DECLARE_DELAYED_REF_SINGLETON(DeviceStatusClient)
  3. public:
  4.     ~DeviceStatusClient();
  5.     ErrCode Connect();
  6.     sptr deathRecipient_ { nullptr };
  7. };
复制代码
步骤4. 创建消亡信息接收者对象,并获取服务端(远端)对象

客户端首次Connect()时,通过服务端 MSDP_DEVICESTATUS_SERVICE_ID 获取被监听者的对象,然后将接收者添加给它。
  1. ErrCode DeviceStatusClient::Connect()
  2. {
  3.     sptr sa = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
  4.     sptr remoteObject = sa->CheckSystemAbility(MSDP_DEVICESTATUS_SERVICE_ID);
  5.     deathRecipient_ = sptr(new (std::nothrow) DeviceStatusDeathRecipient());
  6.     if (remoteObject->IsProxyObject()) {
  7.         remoteObject->AddDeathRecipient(deathRecipient_);
  8.     }
  9.     proxy_ = iface_cast(remoteObject);
  10.     return RET_OK;
  11. }
复制代码
步骤5. 客户端析构时,删除消亡信息接收者
  1. DeviceStatusClient::~DeviceStatusClient()
  2. {
  3.     if (proxy_ != nullptr) {
  4.         auto remoteObject = proxy_->AsObject();
  5.         if (remoteObject != nullptr) {
  6.             remoteObject->RemoveDeathRecipient(deathRecipient_);
  7.         }
  8.     }
  9. }
复制代码
例3:服务端监听客户端消亡

在本端要做的事情

定义接收者对象、添加接收者、删除接收者、处理消亡事件
步骤1. 在服务侧(本端)定义一个消亡信息接收者对象
  1. class SensorService : public SystemAbility, public StreamServer, public SensorServiceStub {
  2.     DECLARE_SYSTEM_ABILITY(SensorService)
  3.     SENSOR_DECLARE_DELAYED_SP_SINGLETON(SensorService);
  4. public:
  5.     void ProcessDeathObserver(const wptr &object);
  6. private:
  7.     DISALLOW_COPY_AND_MOVE(SensorService);
  8.     void RegisterClientDeathRecipient(sptr sensorClient, int32_t pid);
  9.     void UnregisterClientDeathRecipient(sptr sensorClient);
  10.     // death recipient of sensor client
  11.     sptr deathRecipient_ = nullptr; // 定义消亡信息接收者对象
  12. };
复制代码
步骤2. 在服务侧(本端),首先构建接收者,然后将接收者添加给传入的远端(客户侧)对象
  1. void SensorService::RegisterClientDeathRecipient(sptr sensorClient, int32_t pid)
  2. {
  3.     if (deathRecipient_ == nullptr) {
  4.         deathRecipient_ = new (std::nothrow) DeathRecipientTemplate(*const_cast(this));
  5.         CHKPV(deathRecipient_);
  6.     }
  7.     sensorClient->AddDeathRecipient(deathRecipient_);
  8.     clientInfo_.SaveClientPid(sensorClient, pid);
  9. }
复制代码
消亡信息接收者的模板类如下定义,构造时将类对象传入,放在privateData_。
  1. #include "iremote_object.h"
  2. template
  3. class DeathRecipientTemplate : public IRemoteObject::DeathRecipient {
  4. public:
  5.     explicit DeathRecipientTemplate(T &privateData) : privateData_(privateData) {};
  6.     virtual ~DeathRecipientTemplate() = default;
  7.     // 被监听者消亡后被调起
  8.     virtual void OnRemoteDied(const wptr &object)
  9.     {
  10.         privateData_.ProcessDeathObserver(object);
  11.     };
  12. private:
  13.     T &privateData_;// 构造时传入
  14. };
复制代码
步骤3. 在服务侧(本端),将接收者从远端(客户侧)对象中删除
  1. void SensorService::UnregisterClientDeathRecipient(sptr sensorClient)
  2. {
  3.     sensorClient->RemoveDeathRecipient(deathRecipient_);
  4. }
复制代码
步骤4. 在服务侧(本端),处理消亡事件
  1. void SensorService::ProcessDeathObserver(const wptr &object)
  2. {
  3.     sptr client = object.promote();
  4.     int32_t pid = clientInfo_.FindClientPid(client);
  5.     if (pid == INVALID_PID) {
  6.         LOGE("pid is invalid");
  7.         return;
  8.     }
  9.     LOGI("pid is %{public}d", pid);
  10. }
复制代码
在远端要做的事情

步骤1. 在客户侧(远端被监听),创建对象并且一路传递给服务侧
  1. int32_t SensorServiceClient::TransferDataChannel(sptr sensorDataChannel)
  2. {
  3.     if (sensorClientStub_ == nullptr) {
  4.         sensorClientStub_ = new (std::nothrow) SensorClientStub(); // 创建被监听者对象
  5.     }
  6.     auto sm = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
  7.     auto object = sm->GetSystemAbility(SENSOR_SERVICE_ABILITY_ID);
  8.     proxy_ = iface_cast(object);
  9.     auto remoteObject = sensorClientStub_->AsObject();
  10.     ret = proxy_->TransferDataChannel(sensorDataChannel, remoteObject);
  11.     return ret;
  12. }
  13. ErrCode SensorService::TransferDataChannel(const sptr &sensorBasicDataChannel,
  14.                                            const sptr &sensorClient)
  15. {
  16.     RegisterClientDeathRecipient(sensorClient, pid);
  17.     return ERR_OK;
  18. }
复制代码
步骤2. 在客户侧(远端被监听),被监听者对象是如何被构建出来是关键的一环

首先,创建一个ISensorClient类,必须从IRemoteBroker继承;其次,创建一个SensorClientStub类,必须从IRemoteStub继承;最后,重写 OnRemoteRequest()函数,函数内什么有用的事情也没做。
其实,客户端Client为自己构建一个Stub作为自己的“影子”,然后将他传递给服务端(本端)作为被监听对象。
  1. #include "iremote_broker.h"
  2. class ISensorClient : public IRemoteBroker {
  3. public:
  4.     ISensorClient() = default;
  5.     virtual ~ISensorClient() = default;
  6.     DECLARE_INTERFACE_DESCRIPTOR(u"ISensorClient");
  7. };
  8. class SensorClientStub : public IRemoteStub {
  9. public:
  10.     SensorClientStub() = default;
  11.     virtual ~SensorClientStub() = default;
  12.     virtual int32_t OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply,
  13.                                     MessageOption &option) override;
  14. };
  15. int32_t SensorClientStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply,
  16.                                           MessageOption &option)
  17. {
  18.     std::u16string descriptor = SensorClientStub::GetDescriptor();
  19.     std::u16string remoteDescriptor = data.ReadInterfaceToken();
  20.     if (descriptor != remoteDescriptor) {
  21.         return OBJECT_NULL;
  22.     }
  23.     LOGD("Begin, cmd:%{public}u", code);
  24.     return NO_ERROR;
  25. }
复制代码
例4:服务端监听底层HDI侧消亡

此种情况处理较为简单, 全部代码在服务侧(本端)实现,通过Get()函数直接获取了远端对象。
步骤1. 生成一个消亡信息接收者的模板类

构造时将类对象传入,放在privateData_。
  1. #include "iremote_object.h"
  2. template
  3. class DeathRecipientTemplate : public IRemoteObject::DeathRecipient {
  4. public:
  5.     explicit DeathRecipientTemplate(T &privateData) : privateData_(privateData) {};
  6.     virtual ~DeathRecipientTemplate() = default;
  7.     // 被监听者消亡后被调起
  8.     virtual void OnRemoteDied(const wptr &object)
  9.     {
  10.         privateData_.ProcessDeathObserver(object);
  11.     };
  12. private:
  13.     T &privateData_;// 构造时传入
  14. };
复制代码
步骤2. 定义消亡信息接收者对象
  1. class HdiLightConnection : public ILightHdiConnection {
  2. public:
  3.     HdiLightConnection() = default;
  4.     virtual ~HdiLightConnection() {};
  5.     int32_t ConnectHdi() override;
  6.     int32_t DestroyHdiConnection() override;
  7.     void ProcessDeathObserver(const wptr &object);// 由消亡信息接收者类调起
  8. private:
  9.     DISALLOW_COPY_AND_MOVE(HdiLightConnection);
  10.     sptr hdiDeathObserver_ = nullptr; // 定义接收者对象
  11.     sptr lightInterface_ = nullptr;
  12.     void RegisterHdiDeathRecipient();
  13.     void UnregisterHdiDeathRecipient();
  14. };
复制代码
步骤3. 首次链接底层硬件服务时,获取底层远端对象(被监听者)lightInterface_
  1. int32_t HdiLightConnection::ConnectHdi()
  2. {
  3.     lightInterface_ = ILightInterface::Get();
  4.     if (lightInterface_ != nullptr) {
  5.         RegisterHdiDeathRecipient();
  6.         return ERR_OK;
  7.     }
  8.     return ERR_INVALID_VALUE;
  9. }
复制代码
步骤4. 构建消亡信息接收者对象 hdiDeathObserver_

顺便将接收者添加至远端被监听者 lightInterface_
  1. void HdiLightConnection::RegisterHdiDeathRecipient()
  2. {
  3.     if (hdiDeathObserver_ == nullptr) {
  4.         hdiDeathObserver_ = new (std::nothrow) DeathRecipientTemplate(*const_cast(this));
  5.     }
  6.     OHOS::HDI::hdi_objcast(lightInterface_)->AddDeathRecipient(hdiDeathObserver_);
  7. }
复制代码
步骤5. 本端不再监听远端时,将消亡信息接收者从远端(被监听者)移除
  1. void HdiLightConnection::UnregisterHdiDeathRecipient()
  2. {
  3.     OHOS::HDI::hdi_objcast(lightInterface_)->RemoveDeathRecipient(hdiDeathObserver_);
  4. }
复制代码
步骤6. 远端(被监听者)消亡,将消亡信息接收者从远端(被监听者)移除
  1. void HdiLightConnection::ProcessDeathObserver(const wptr &object)
  2. {
  3.     sptr hdiService = object.promote();
  4.     hdiService->RemoveDeathRecipient(hdiDeathObserver_);
  5. }
复制代码
三、传感器服务端监听不同客户端消亡的处理实例

参考链接:https://gitee.com/openharmony/sensors_miscdevice/pulls/314


四、结论

本文全面描述了本端监听远端消亡,接收到消亡信息的后续处理实现。
定义消亡信息接收者,向远端添加、移除接收者以及处理消亡事件都比较简单明了。但是,如何获取被监听的远端对象差异较大,尤其是服务端获取客户端(远端)消亡对象较为复杂。客户端Client为自己构建一个Stub作为自己的“影子”,然后将他传递给服务端(本端)作为被监听对象。
各个示例如何获取远端对象汇总如下,以资参考:
场景扼要结论如何获取远端对象
例1:服务侧监听客户侧消亡客户端再构造一个远端Stub对象,较为复杂class IRemoteClientObject : public IRemoteBroker {};class ClientStubObject :  public IRemoteStub {};  sptr object = new (std::nothrow) ClientStubObject(env);
例2:客户端监听服务端消亡IPC 天然支持client监听server,做法简单sptr sa = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();sptr remoteObject = sa->CheckSystemAbility(MSDP_DEVICESTATUS_SERVICE_ID);
例3:服务端监听客户端消亡客户端再构造一个远端Stub对象,较为复杂class SensorClientStub : public IRemoteStub {};object = new (std::nothrow) SensorClientStub(); // 创建被监听者对象
例4:服务端监听底层HDI侧消亡底层HDI侧封装的很好,做法简单object = ILightInterface::Get();

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

2

主题

4

回帖

18

积分

新手上路

积分
18
发表于 2025-3-30 12:11:28 | 显示全部楼层
受教了
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表