OpenHarmony开发者论坛

标题: 【SUBJECT技术】OpenHarmony 在一台设备从本端监听远端消亡的实现方法 [打印本页]

作者: 诚迈_雨哥    时间: 2024-6-3 16:18
标题: 【SUBJECT技术】OpenHarmony 在一台设备从本端监听远端消亡的实现方法
[md]# 背景

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

以下实例全部基于"client,proxy→stub,service"架构。

# 目录

* [1.原理介绍](#一、原理介绍)
* [2.几种情况的重点说明](#二、几种情况的重点说明)
  * [2.1 例1:服务侧监听客户侧消亡](##例1:服务侧监听客户侧消亡)
  * [2.2 例2:客户端监听服务端消亡](##例2:客户端监听服务端消亡)
  * [2.3 例3:服务端监听客户端消亡](##例3:服务端监听客户端消亡)
  * [2.4 例4:服务端监听底层HDI侧消亡](##例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<DeathRecipient> &recipient);    | bool       | 订阅远端Stub对象状态。     |
| RemoveDeathRecipient(const sptr<DeathRecipient> &recipient); | bool       | 取消订阅远端Stub对象状态。 |
| OnRemoteDied(const wptr<IRemoteObject> &object);             | void       | 当远端Stub对象消亡时回调。 |

## 4. 调用序列图

![SequenceDeathRecipient.PNG](https://forums-obs.openharmony.c ... ywjwvnvjcmzzmmv.png "SequenceDeathRecipient.PNG")

![](./figures/SequenceDeathRecipient.png)

# 二、几种情况的重点说明

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

只要出现 OnRemoteDied() 的地方就是本端,它是用来处理消亡的地方。

## 例1:服务侧监听客户侧消亡

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

### 在远端要做的事情

#### 步骤1. 定义被监听者类 ClientStubObject

```
class IRemoteClientObject : public IRemoteBroker {
public:

    DECLARE_INTERFACE_DESCRIPTOR(u"ohos.xxx.IRemoteClientObject");// 必须存在,不然找不到该对象
};

class ClientStubObject :  public IRemoteStub<IRemoteClientObject> {
public:
    explicit ClientStubObject(napi_env env) : env_(env) {}
    virtual ~ClientStubObject() {};
    int32_t OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override;
};
```

#### 步骤2. 创建被监听者类对象

```
    sptr<IRemoteClientObject> object = new (std::nothrow) ClientStubObject(env);
```

#### 步骤3. 把 object 一路传递给服务侧

```
void DeviceStatusSrvProxy::Subscribe(sptr<IRemoteClientObject> object)
{
    sptr<IRemoteObject> remote = Remote();
    MessageParcel data;
    WRITEREMOTEOBJECT(data, object->AsObject());
    MessageParcel reply;
    MessageOption option;
    int32_t ret = remote->SendRequest(static_cast<uint32_t>(DeviceInterfaceCode:EVICESTATUS_SUBSCRIBE),
        data, reply, option);
}

int32_t DeviceStatusSrvStub::SubscribeStub(MessageParcel &data, MessageParcel &reply)
{
    sptr<IRemoteObject> obj = data.ReadRemoteObject();
    sptr<IRemoteClientObject> object = iface_cast<IRemoteClientObject>(obj);

    return RET_OK;
}
```

## 例2:客户端监听服务端消亡

此种情况处理较为简单, 全部代码在客户侧(本端)实现。

### 步骤1. 定义消亡信息接收者类

```
    class DeviceStatusDeathRecipient : public IRemoteObject:eathRecipient {
    public:
        DeviceStatusDeathRecipient() = default;
        ~DeviceStatusDeathRecipient() = default;
        void OnRemoteDied(const wptr<IRemoteObject> &remote);

    private:
        DISALLOW_COPY_AND_MOVE(DeviceStatusDeathRecipient);
    };
```

### 步骤2. 消亡信息接收到的处理

```
void DeviceStatusClient:eviceStatusDeathRecipient::OnRemoteDied(const wptr<IRemoteObject> &remote)
{
    DeviceStatusClient::GetInstance().ResetProxy(remote);
    LOGD("Recv death notice");
}
```

### 步骤3. 客户端定义消亡信息接收者对象

```
class DeviceStatusClient final : public DelayedRefSingleton<DeviceStatusClient> {
    DECLARE_DELAYED_REF_SINGLETON(DeviceStatusClient)
public:
    ~DeviceStatusClient();
    ErrCode Connect();

    sptr<IRemoteObject:eathRecipient> deathRecipient_ { nullptr };
};
```

### 步骤4. 创建消亡信息接收者对象,并获取服务端(远端)对象

客户端首次Connect()时,通过服务端 MSDP_DEVICESTATUS_SERVICE_ID 获取被监听者的对象,然后将接收者添加给它。

```
ErrCode DeviceStatusClient::Connect()
{
    sptr<ISystemAbilityManager> sa = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    sptr<IRemoteObject> remoteObject = sa->CheckSystemAbility(MSDP_DEVICESTATUS_SERVICE_ID);

    deathRecipient_ = sptr<IRemoteObject:eathRecipient>(new (std::nothrow) DeviceStatusDeathRecipient());

    if (remoteObject->IsProxyObject()) {
        remoteObject->AddDeathRecipient(deathRecipient_);
    }
    proxy_ = iface_cast<Idevicestatus>(remoteObject);

    return RET_OK;
}
```

### 步骤5. 客户端析构时,删除消亡信息接收者

```
DeviceStatusClient::~DeviceStatusClient()
{
    if (proxy_ != nullptr) {
        auto remoteObject = proxy_->AsObject();
        if (remoteObject != nullptr) {
            remoteObject->RemoveDeathRecipient(deathRecipient_);
        }
    }
}
```

## 例3:服务端监听客户端消亡

### 在本端要做的事情

定义接收者对象、添加接收者、删除接收者、处理消亡事件

#### 步骤1. 在服务侧(本端)定义一个消亡信息接收者对象

```
class SensorService : public SystemAbility, public StreamServer, public SensorServiceStub {
    DECLARE_SYSTEM_ABILITY(SensorService)
    SENSOR_DECLARE_DELAYED_SP_SINGLETON(SensorService);

public:
    void ProcessDeathObserver(const wptr<IRemoteObject> &object);

private:
    DISALLOW_COPY_AND_MOVE(SensorService);

    void RegisterClientDeathRecipient(sptr<IRemoteObject> sensorClient, int32_t pid);
    void UnregisterClientDeathRecipient(sptr<IRemoteObject> sensorClient);

    // death recipient of sensor client
    sptr<IRemoteObject:eathRecipient> deathRecipient_ = nullptr; // 定义消亡信息接收者对象
};
```

#### 步骤2. 在服务侧(本端),首先构建接收者,然后将接收者添加给传入的远端(客户侧)对象

```
void SensorService::RegisterClientDeathRecipient(sptr<IRemoteObject> sensorClient, int32_t pid)
{
    if (deathRecipient_ == nullptr) {
        deathRecipient_ = new (std::nothrow) DeathRecipientTemplate(*const_cast<SensorService *>(this));
        CHKPV(deathRecipient_);
    }
    sensorClient->AddDeathRecipient(deathRecipient_);
    clientInfo_.SaveClientPid(sensorClient, pid);
}
```

消亡信息接收者的模板类如下定义,构造时将类对象传入,放在privateData_。

```
#include "iremote_object.h"

template<typename T>
class DeathRecipientTemplate : public IRemoteObject:eathRecipient {
public:
    explicit DeathRecipientTemplate(T &privateData) : privateData_(privateData) {};
    virtual ~DeathRecipientTemplate() = default;
    // 被监听者消亡后被调起
    virtual void OnRemoteDied(const wptr<IRemoteObject> &object)
    {
        privateData_.ProcessDeathObserver(object);
    };

private:
    T &privateData_;// 构造时传入
};
```

#### 步骤3. 在服务侧(本端),将接收者从远端(客户侧)对象中删除

```
void SensorService::UnregisterClientDeathRecipient(sptr<IRemoteObject> sensorClient)
{
    sensorClient->RemoveDeathRecipient(deathRecipient_);
}
```

#### 步骤4. 在服务侧(本端),处理消亡事件

```
void SensorService:rocessDeathObserver(const wptr<IRemoteObject> &object)
{
    sptr<IRemoteObject> client = object.promote();
    int32_t pid = clientInfo_.FindClientPid(client);
    if (pid == INVALID_PID) {
        LOGE("pid is invalid");
        return;
    }
    LOGI("pid is %{public}d", pid);
}
```

### 在远端要做的事情

#### 步骤1. 在客户侧(远端被监听),创建对象并且一路传递给服务侧

```
int32_t SensorServiceClient::TransferDataChannel(sptr<SensorDataChannel> sensorDataChannel)
{
    if (sensorClientStub_ == nullptr) {
        sensorClientStub_ = new (std::nothrow) SensorClientStub(); // 创建被监听者对象
    }
    auto sm = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    auto object = sm->GetSystemAbility(SENSOR_SERVICE_ABILITY_ID);

    proxy_ = iface_cast<ISensorService>(object);

    auto remoteObject = sensorClientStub_->AsObject();
    ret = proxy_->TransferDataChannel(sensorDataChannel, remoteObject);

    return ret;
}

ErrCode SensorService::TransferDataChannel(const sptr<SensorBasicDataChannel> &sensorBasicDataChannel,
                                           const sptr<IRemoteObject> &sensorClient)
{
    RegisterClientDeathRecipient(sensorClient, pid);
    return ERR_OK;
}
```

#### 步骤2. 在客户侧(远端被监听),被监听者对象是如何被构建出来是关键的一环

首先,创建一个ISensorClient类,必须从IRemoteBroker继承;
其次,创建一个SensorClientStub类,必须从IRemoteStub<ISensorClient>继承;
最后,重写 OnRemoteRequest()函数,函数内什么有用的事情也没做。

其实,客户端Client为自己构建一个Stub作为自己的“影子”,然后将他传递给服务端(本端)作为被监听对象。

```
#include "iremote_broker.h"

class ISensorClient : public IRemoteBroker {
public:
    ISensorClient() = default;
    virtual ~ISensorClient() = default;
    DECLARE_INTERFACE_DESCRIPTOR(u"ISensorClient");
};

class SensorClientStub : public IRemoteStub<ISensorClient> {
public:
    SensorClientStub() = default;
    virtual ~SensorClientStub() = default;
    virtual int32_t OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply,
                                    MessageOption &option) override;
};

int32_t SensorClientStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply,
                                          MessageOption &option)
{
    std::u16string descriptor = SensorClientStub::GetDescriptor();
    std::u16string remoteDescriptor = data.ReadInterfaceToken();
    if (descriptor != remoteDescriptor) {
        return OBJECT_NULL;
    }
    LOGD("Begin, cmd:%{public}u", code);
    return NO_ERROR;
}

```

## 例4:服务端监听底层HDI侧消亡

此种情况处理较为简单, 全部代码在服务侧(本端)实现,通过Get()函数直接获取了远端对象。

### 步骤1. 生成一个消亡信息接收者的模板类

构造时将类对象传入,放在privateData_。

```
#include "iremote_object.h"

template<typename T>
class DeathRecipientTemplate : public IRemoteObject:eathRecipient {
public:
    explicit DeathRecipientTemplate(T &privateData) : privateData_(privateData) {};
    virtual ~DeathRecipientTemplate() = default;
    // 被监听者消亡后被调起
    virtual void OnRemoteDied(const wptr<IRemoteObject> &object)
    {
        privateData_.ProcessDeathObserver(object);
    };

private:
    T &privateData_;// 构造时传入
};
```

### 步骤2. 定义消亡信息接收者对象

```
class HdiLightConnection : public ILightHdiConnection {
public:
    HdiLightConnection() = default;
    virtual ~HdiLightConnection() {};
    int32_t ConnectHdi() override;
  
    int32_t DestroyHdiConnection() override;
    void ProcessDeathObserver(const wptr<IRemoteObject> &object);// 由消亡信息接收者类调起

private:
    DISALLOW_COPY_AND_MOVE(HdiLightConnection);
    sptr<IRemoteObject:eathRecipient> hdiDeathObserver_ = nullptr; // 定义接收者对象
    sptr<ILightInterface> lightInterface_ = nullptr;
    void RegisterHdiDeathRecipient();
    void UnregisterHdiDeathRecipient();
};
```

### 步骤3. 首次链接底层硬件服务时,获取底层远端对象(被监听者)lightInterface_

```
int32_t HdiLightConnection::ConnectHdi()
{
    lightInterface_ = ILightInterface::Get();
    if (lightInterface_ != nullptr) {
        RegisterHdiDeathRecipient();
        return ERR_OK;
    }
   
    return ERR_INVALID_VALUE;
}
```

### 步骤4. 构建消亡信息接收者对象 hdiDeathObserver_

顺便将接收者添加至远端被监听者 lightInterface_

```
void HdiLightConnection::RegisterHdiDeathRecipient()
{
    if (hdiDeathObserver_ == nullptr) {
        hdiDeathObserver_ = new (std::nothrow) DeathRecipientTemplate(*const_cast<HdiLightConnection *>(this));
    }
    OHOS::HDI::hdi_objcast<ILightInterface>(lightInterface_)->AddDeathRecipient(hdiDeathObserver_);
}
```

### 步骤5. 本端不再监听远端时,将消亡信息接收者从远端(被监听者)移除

```
void HdiLightConnection::UnregisterHdiDeathRecipient()
{
    OHOS::HDI::hdi_objcast<ILightInterface>(lightInterface_)->RemoveDeathRecipient(hdiDeathObserver_);
}
```

### 步骤6. 远端(被监听者)消亡,将消亡信息接收者从远端(被监听者)移除

```
void HdiLightConnection:rocessDeathObserver(const wptr<IRemoteObject> &object)
{
    sptr<IRemoteObject> hdiService = object.promote();

    hdiService->RemoveDeathRecipient(hdiDeathObserver_);
}
```

# 三、传感器服务端监听不同客户端消亡的处理实例

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

![DeathRecipient_PR.png](https://forums-obs.openharmony.c ... ukjhjmoqsho29ou.png "DeathRecipient_PR.png")

![](./figures/deathRecipient_pr.png)

# 四、结论

本文全面描述了本端监听远端消亡,接收到消亡信息的后续处理实现。

定义消亡信息接收者,向远端添加、移除接收者以及处理消亡事件都比较简单明了。但是,如何获取被监听的远端对象差异较大,尤其是服务端获取客户端(远端)消亡对象较为复杂。客户端Client为自己构建一个Stub作为自己的“影子”,然后将他传递给服务端(本端)作为被监听对象。

各个示例如何获取远端对象汇总如下,以资参考:

| 场景                         | 扼要结论                               | 如何获取远端对象                                                                                                                                                                                                |
| ---------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 例1:服务侧监听客户侧消亡    | 客户端再构造一个远端Stub对象,较为复杂 | class IRemoteClientObject : public IRemoteBroker {};<br>class ClientStubObject :  public IRemoteStub<IRemoteClientObject> {};<br>  sptr<IRemoteClientObject> object = new (std::nothrow) ClientStubObject(env); |
| 例2:客户端监听服务端消亡    | IPC 天然支持client监听server,做法简单 | sptr<ISystemAbilityManager> sa = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();<br>sptr<IRemoteObject> remoteObject = sa->CheckSystemAbility(MSDP_DEVICESTATUS_SERVICE_ID);               |
| 例3:服务端监听客户端消亡    | 客户端再构造一个远端Stub对象,较为复杂 | class SensorClientStub : public IRemoteStub<ISensorClient> {};<br>object = new (std::nothrow) SensorClientStub(); // 创建被监听者对象                                                                           |
| 例4:服务端监听底层HDI侧消亡 | 底层HDI侧封装的很好,做法简单           | object = ILightInterface::Get();                                                                                                                                                                                |
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5