[经验分享] 【SUBJECT技术】OpenHarmony 在一台设备从本端监听远端消亡的实现方法 原创 精华 全局置顶

诚迈_雨哥 显示全部楼层 发表于 2024-6-3 16:18:16

背景

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

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

目录

一、原理介绍

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

二、几种情况的重点说明

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

只要出现 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<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::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_);
}

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

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

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

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

精彩评论1

yeyao

沙发 发表于 6 天前
受教了

Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

返回顶部