1# 远端状态订阅开发实例 2 3IPC/RPC提供对远端Stub对象状态的订阅机制,在远端Stub对象消亡时,可触发消亡通知告诉本地Proxy对象。这种状态通知订阅需要调用特定接口完成,当不再需要订阅时也需要调用特定接口取消。使用这种订阅机制的用户,需要实现消亡通知接口DeathRecipient并实现onRemoteDied方法清理资源。该方法会在远端Stub对象所在进程消亡或所在设备离开组网时被回调。值得注意的是,调用这些接口有一定的顺序。首先,需要Proxy订阅Stub消亡通知,若在订阅期间Stub状态正常,则在不再需要时取消订阅;若在订阅期间Stub所在进程退出或者所在设备退出组网,则会自动触发Proxy自定义的后续操作。 4 5## 使用场景 6 7这种订阅机制适用于本地Proxy对象需要感知远端Stub对象所在进程消亡,或所在设备离开组网的场景。当Proxy感知到Stub端消亡后,可适当清理本地资源。此外,RPC目前不提供匿名Stub对象的消亡通知,即只有向SAMgr注册过的服务才能被订阅消亡通知,IPC则支持匿名对象的消亡通知。 8 9 10## Native侧接口 11 12| 接口名 | 描述 | 13| ------------------------------------------------------------------- | ------------------------- | 14| bool AddDeathRecipient(const sptr\<DeathRecipient> &recipient); | 订阅远端Stub对象状态。 | 15| bool RemoveDeathRecipient(const sptr\<DeathRecipient> &recipient); | 取消订阅远端Stub对象状态。 | 16| void OnRemoteDied(const wptr\<IRemoteObject> &object); | 当远端Stub对象死亡时回调。 | 17 18### 参考代码 19 20```C++ 21#include "iremote_broker.h" 22#include "iremote_stub.h" 23 24//定义消息码 25enum { 26 TRANS_ID_PING_ABILITY = 5, 27 TRANS_ID_REVERSED_MONITOR 28}; 29 30const std::string DESCRIPTOR = "test.ITestAbility"; 31 32class ITestService : public IRemoteBroker { 33public: 34 // DECLARE_INTERFACE_DESCRIPTOR是必需的,入参需使用std::u16string; 35 DECLARE_INTERFACE_DESCRIPTOR(to_utf16(DESCRIPTOR)); 36 virtual int TestPingAbility(const std::u16string &dummy) = 0; // 定义业务函数 37}; 38 39class TestServiceProxy : public IRemoteProxy<ITestAbility> { 40public: 41 explicit TestAbilityProxy(const sptr<IRemoteObject> &impl); 42 virtual int TestPingAbility(const std::u16string &dummy) override; 43 int TestAnonymousStub(); 44private: 45 static inline BrokerDelegator<TestAbilityProxy> delegator_; // 方便后续使用iface_cast宏 46}; 47 48TestServiceProxy::TestServiceProxy(const sptr<IRemoteObject> &impl) 49 : IRemoteProxy<ITestAbility>(impl) 50{ 51} 52 53int TestServiceProxy::TestPingAbility(const std::u16string &dummy){ 54 MessageOption option; 55 MessageParcel dataParcel, replyParcel; 56 dataParcel.WriteString16(dummy); 57 int error = PeerHolder::Remote()->SendRequest(TRANS_ID_PING_ABILITY, dataParcel, replyParcel, option); 58 int result = (error == ERR_NONE) ? replyParcel.ReadInt32() : -1; 59 return result; 60} 61``` 62 63```c++ 64#include "iremote_object.h" 65 66class TestDeathRecipient : public IRemoteObject::DeathRecipient { 67public: 68 virtual void OnRemoteDied(const wptr<IRemoteObject>& remoteObject); 69} 70 71void TestDeathRecipient::OnRemoteDied(const wptr<IRemoteObject>& remoteObject) 72{ 73} 74``` 75 76```c++ 77sptr<IPCObjectProxy> object = new IPCObjectProxy(1, to_utf16(DESCRIPTOR)); 78sptr<IRemoteObject::DeathRecipient> deathRecipient (new TestDeathRecipient()); // 构造一个消亡通知对象 79bool result = object->AddDeathRecipient(deathRecipient); // 注册消亡通知 80result = object->RemoveDeathRecipient(deathRecipient); // 移除消亡通知 81``` 82 83## ArkTS侧接口 84 85| 接口名 | 返回值类型 | 功能描述 | 86| ------------------------------------------------------------ | ---------- | ------------------------------------------------------------ | 87| [registerDeathRecipient](../reference/apis/js-apis-rpc.md#registerdeathrecipient9-1) | void | 注册用于接收远程对象消亡通知的回调,增加 proxy 对象上的消亡通知。 | 88| [unregisterDeathRecipient](../reference/apis/js-apis-rpc.md#unregisterdeathrecipient9-1) | void | 注销用于接收远程对象消亡通知的回调。 | 89| [onRemoteDied](../reference/apis/js-apis-rpc.md#onremotedied) | void | 在成功添加死亡通知订阅后,当远端对象死亡时,将自动调用本方法。 | 90 91### 获取context 92 93Stage模型在连接服务前需要先获取context 94 95```ts 96import UIAbility from '@ohos.app.ability.UIAbility'; 97import Want from '@ohos.app.ability.Want'; 98import hilog from '@ohos.hilog'; 99import AbilityConstant from '@ohos.app.ability.AbilityConstant'; 100import window from '@ohos.window'; 101 102export default class MainAbility extends UIAbility { 103 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 104 hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onCreate'); 105 let context = this.context; 106 } 107 onDestroy() { 108 hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onDestroy'); 109 } 110 onWindowStageCreate(windowStage: window.WindowStage) { 111 // Main window is created, set main page for this ability 112 hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onWindowStageCreate'); 113 } 114 onWindowStageDestroy() { 115 // Main window is destroyed, release UI related resources 116 hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onWindowStageDestroy'); 117 } 118 onForeground() { 119 // Ability has brought to foreground 120 hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onForeground'); 121 } 122 onBackground() { 123 // Ability has back to background 124 hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onBackground'); 125 } 126} 127``` 128 129### 参考代码 130 131```ts 132// 仅FA模型需要导入@ohos.ability.featureAbility 133// import FA from "@ohos.ability.featureAbility"; 134import Want from '@ohos.app.ability.Want'; 135import common from '@ohos.app.ability.common'; 136import rpc from '@ohos.rpc'; 137import hilog from '@ohos.hilog'; 138 139let proxy: rpc.IRemoteObject | undefined; 140let connect: common.ConnectOptions = { 141 onConnect: (elementName, remoteProxy) => { 142 hilog.info(0x0000, 'testTag', 'RpcClient: js onConnect called.'); 143 proxy = remoteProxy; 144 }, 145 onDisconnect: (elementName) => { 146 hilog.info(0x0000, 'testTag', 'RpcClient: onDisconnect'); 147 }, 148 onFailed: () => { 149 hilog.info(0x0000, 'testTag', 'RpcClient: onFailed'); 150 } 151}; 152let want: Want = { 153 bundleName: "com.ohos.server", 154 abilityName: "com.ohos.server.EntryAbility", 155}; 156// FA模型通过此方法连接服务 157// FA.connectAbility(want, connect); 158 159this.context.connectServiceExtensionAbility(want, connect); 160``` 161 162上述onConnect回调函数中的proxy对象需要等ability异步连接成功后才会被赋值,然后才可调用proxy对象的[unregisterDeathRecipient](../reference/apis/js-apis-rpc.md#unregisterdeathrecipient9-1)接口方法注销死亡回调 163 164```ts 165import rpc from '@ohos.rpc'; 166import hilog from '@ohos.hilog'; 167 168class MyDeathRecipient implements rpc.DeathRecipient{ 169 onRemoteDied() { 170 hilog.info(0x0000, 'testTag', 'server died'); 171 } 172} 173let deathRecipient = new MyDeathRecipient(); 174if (proxy != undefined) { 175 proxy.registerDeathRecipient(deathRecipient, 0); 176 proxy.unregisterDeathRecipient(deathRecipient, 0); 177} 178``` 179 180## Stub感知Proxy消亡(匿名Stub的使用) 181 182正向的消亡通知是Proxy感知Stub的状态,若想达到反向的死消亡通知,即Stub感知Proxy的状态,可以巧妙的利用正向消亡通知。如两个进程A(原Stub所在进程)和B(原Proxy所在进程),进程B在获取到进程A的Proxy对象后,在B进程新建一个匿名Stub对象(匿名指未向SAMgr注册),可称之为回调Stub,再通过SendRequest接口将回调Stub传给进程A的原Stub。这样一来,进程A便获取到了进程B的回调Proxy。当进程B消亡或B所在设备离开组网时,回调Stub会消亡,回调Proxy会感知,进而通知给原Stub,便实现了反向消亡通知。 183 184注意: 185 186> 反向死亡通知仅限设备内跨进程通信使用,不可用于跨设备。 187 188> 当匿名Stub对象没有被任何一个Proxy指向的时候,内核会自动回收。 189 190### 参考代码 191 192```c++ 193// Proxy 194int TestAbilityProxy::TestAnonymousStub() 195{ 196 MessageOption option; 197 MessageParcel dataParcel, replyParcel; 198 dataParcel.UpdateDataVersion(Remote()); 199 dataParcel.WriteRemoteObject(new TestAbilityStub()); 200 int error = Remote()->SendRequest(TRANS_ID_REVERSED_MONITOR,dataParcel, replyParcel, option); 201 int result = (error == ERR_NONE) ? replyParcel.ReadInt32() : -1; 202 return result; 203} 204 205// Stub 206 207int TestAbilityStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) 208{ 209 switch (code) { 210 case TRANS_ID_REVERSED_MONITOR: { 211 sptr<IRemoteObject> obj = data.ReadRemoteObject(); 212 if (obj == nullptr) { 213 reply.WriteInt32(ERR_NULL_OBJECT); 214 return ERR_NULL_OBJECT; 215 } 216 bool result = obj->AddDeathRecipient(new TestDeathRecipient()); 217 result ? reply.WriteInt32(ERR_NONE) : reply.WriteInt32(-1); 218 break; 219 } 220 default: 221 break; 222 } 223 return ERR_NONE; 224} 225``` 226 227