OpenHarmony 如何在一台设备从本端监听远端消亡的实现方法

654 篇文章 4 订阅
648 篇文章 6 订阅


背景

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

一、原理介绍

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
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::DEVICESTATUS_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::DeathRecipient {
    public:
        DeviceStatusDeathRecipient() = default;
        ~DeviceStatusDeathRecipient() = default;
        void OnRemoteDied(const wptr<IRemoteObject> &remote);

    private:
        DISALLOW_COPY_AND_MOVE(DeviceStatusDeathRecipient);
    };

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

void DeviceStatusClient::DeviceStatusDeathRecipient::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::DeathRecipient> 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::DeathRecipient>(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::DeathRecipient> 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::DeathRecipient {
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::ProcessDeathObserver(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继承;
最后,重写 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::DeathRecipient {
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::DeathRecipient> 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::ProcessDeathObserver(const wptr<IRemoteObject> &object)
{
    sptr<IRemoteObject> hdiService = object.promote();

    hdiService->RemoveDeathRecipient(hdiDeathObserver_);
}

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

四、结论

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

定义消亡信息接收者,向远端添加、移除接收者以及处理消亡事件都比较简单明了。但是,如何获取被监听的远端对象差异较大,尤其是服务端获取客户端(远端)消亡对象较为复杂。客户端 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();

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值