diff --git a/zh-cn/application-dev/connectivity/figures/075sd302-aeb9-481a-bb8f-e552sdb61ead.PNG b/zh-cn/application-dev/connectivity/figures/075sd302-aeb9-481a-bb8f-e552sdb61ead.PNG new file mode 100644 index 0000000000000000000000000000000000000000..1f3ad71eecf43df9dbb1779be5940bf7152aa57a Binary files /dev/null and b/zh-cn/application-dev/connectivity/figures/075sd302-aeb9-481a-bb8f-e552sdb61ead.PNG differ diff --git a/zh-cn/application-dev/connectivity/ipc-rpc-development-guideline.md b/zh-cn/application-dev/connectivity/ipc-rpc-development-guideline.md index dd2d755a1a7717d427b7f7ee67162a5cd324ff03..47366f9ed55ec3798ea2b465a74ca3e4b18050f0 100755 --- a/zh-cn/application-dev/connectivity/ipc-rpc-development-guideline.md +++ b/zh-cn/application-dev/connectivity/ipc-rpc-development-guideline.md @@ -11,42 +11,55 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信, | 类/接口 | 方法 | 功能说明 | | -------- | -------- | -------- | -| [IRemoteBroker](../reference/apis/js-apis-rpc.md#iremotebroker) | sptr<IRemoteObject> AsObject() | 返回通信对象。派生类需要实现,Stub端返回RemoteObject对象本身,Proxy端返回代理对象。 | +| [IRemoteBroker](../reference/apis/js-apis-rpc.md#iremotebroker) | sptr<IRemoteObject> AsObject() | 返回通信对象。Stub端返回RemoteObject对象本身,Proxy端返回代理对象。 | | IRemoteStub | virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) | 请求处理方法,派生类需要重写该方法用来处理Proxy的请求并返回结果。 | -| IRemoteProxy | | 业务Proxy类,派生自IRemoteProxy类。 | +| IRemoteProxy | | 业务的Pory类需要从IRemoteProxy类派生。 | ## 开发步骤 -**Native侧开发步骤** +### **Native侧开发步骤** 1. 添加依赖 -​ sdk依赖: + SDK依赖: - external_deps = [ - "ipc:ipc_core", - ] + ``` + #ipc场景 + external_deps = [ + "ipc:ipc_single", + ] + + #rpc场景 + external_deps = [ + "ipc:ipc_core", + ] + ``` -​ 此外, IPC/RPC依赖的refbase实现在公共基础库下,请增加对utils的依赖: + 此外, IPC/RPC依赖的refbase实现在公共基础库下,请增加对utils的依赖: - deps = [ - “//utils/native/base:utils” - ] - include_dirs = [ - “//utils/native/base/include”, - ] + ``` + external_deps = [ + "c_utils:utils", + ] + ``` 2. 定义IPC接口ITestAbility SA接口继承IPC基类接口IRemoteBroker,接口里定义描述符、业务函数和消息码,其中业务函数在Proxy端和Stub端都需要实现。 - ``` + ```c++ + #include "iremote_broker.h" + + //定义消息码 + const int TRANS_ID_PING_ABILITY = 5 + + const std::string DESCRIPTOR = "test.ITestAbility"; + class ITestAbility : public IRemoteBroker { public: // DECLARE_INTERFACE_DESCRIPTOR是必需的,入参需使用std::u16string; - DECLARE_INTERFACE_DESCRIPTOR("test.ITestAbility"); - int TRANS_ID_PING_ABILITY = 1; // 定义消息码 + DECLARE_INTERFACE_DESCRIPTOR(to_utf16(DESCRIPTOR)); virtual int TestPingAbility(const std::u16string &dummy) = 0; // 定义业务函数 }; ``` @@ -55,7 +68,10 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信, 该类是和IPC框架相关的实现,需要继承 IRemoteStub<ITestAbility>。Stub端作为接收请求的一端,需重写OnRemoteRequest方法用于接收客户端调用。 - ``` + ```c++ + #include "iability_test.h" + #include "iremote_stub.h" + class TestAbilityStub : public IRemoteStub { public: virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override; @@ -79,7 +95,10 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信, ``` 4. 定义服务端业务函数具体实现类TestAbility - ``` + + ```c++ + #include "iability_server_test.h" + class TestAbility : public TestAbilityStub { public: int TestPingAbility(const std::u16string &dummy); @@ -94,7 +113,11 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信, 该类是Proxy端实现,继承IRemoteProxy<ITestAbility>,调用SendRequest接口向Stub端发送请求,对外暴露服务端提供的能力。 - ``` + ```c++ + #include "iability_test.h" + #include "iremote_proxy.h" + #include "iremote_object.h" + class TestAbilityProxy : public IRemoteProxy { public: explicit TestAbilityProxy(const sptr &impl); @@ -115,18 +138,18 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信, int error = Remote()->SendRequest(TRANS_ID_PING_ABILITY, dataParcel, replyParcel, option); int result = (error == ERR_NONE) ? replyParcel.ReadInt32() : -1; return result; - } + } ``` - + 6. SA注册与启动 SA需要将自己的TestAbilityStub实例通过AddSystemAbility接口注册到SystemAbilityManager,设备内与分布式的注册参数不同。 - ``` + ```c++ // 注册到本设备内 auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); samgr->AddSystemAbility(saId, new TestAbility()); - + // 在组网场景下,会被同步到其他设备上 auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); ISystemAbilityManager::SAExtraProp saExtra; @@ -138,7 +161,7 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信, 通过SystemAbilityManager的GetSystemAbility方法可获取到对应SA的代理IRemoteObject,然后构造TestAbilityProxy即可。 - ``` + ```c++ // 获取本设备内注册的SA的proxy sptr samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); sptr remoteObject = samgr->GetSystemAbility(saId); @@ -146,29 +169,35 @@ IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信, // 获取其他设备注册的SA的proxy sptr samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); - sptr remoteObject = samgr->GetSystemAbility(saId, deviceId); // deviceId是指定设备的标识符 + + // networkId是组网场景下对应设备的标识符,可以通过GetLocalNodeDeviceInfo获取 + sptr remoteObject = samgr->GetSystemAbility(saId, networkId); sptr proxy(new TestAbilityProxy(remoteObject)); // 直接构造具体Proxy ``` -**JS侧开发步骤** +### **JS侧开发步骤** 1. 添加依赖 -``` -import rpc from "@ohos.rpc" -import featureAbility from "@ohos.ability.featureAbility" -``` + ```ts + import rpc from "@ohos.rpc" + import featureAbility from "@ohos.ability.featureAbility" + ``` + + -2. 客户端构造变量want,指定要绑定的Ability所在应用的包名、组件名,如果是跨设备的场景,还需要目标设备NetworkId。构造变量connect,指定绑定成功、绑定失败、断开连接时的回调函数。使用featureAbility提供的接口绑定Ability。 +2. 绑定Ability - ``` + 首先,构造变量want,指定要绑定的Ability所在应用的包名、组件名,如果是跨设备的场景,还需要绑定目标设备NetworkId(组网场景下对应设备的标识符,可以使用deviceManager获取目标设备的NetworkId);然后,构造变量connect,指定绑定成功、绑定失败、断开连接时的回调函数;最后,使用featureAbility提供的接口绑定Ability。 + + ```ts import rpc from "@ohos.rpc" import featureAbility from "@ohos.ability.featureAbility" let proxy = null let connectId = null - // 单个设备 + // 单个设备绑定Ability let want = { // 包名和组件名写实际的值 "bundleName": "ohos.rpc.test.server", @@ -190,11 +219,11 @@ import featureAbility from "@ohos.ability.featureAbility" import deviceManager from '@ohos.distributedHardware.deviceManager' function deviceManagerCallback(deviceManager) { let deviceList = deviceManager.getTrustedDeviceListSync() - let deviceId = deviceList[0].deviceId + let networkId = deviceList[0].networkId let want = { "bundleName": "ohos.rpc.test.server", "abilityName": "ohos.rpc.test.service.ServiceAbility", - "deviceId": deviceId, + "networkId": networkId, "flags": 256 } connectId = featureAbility.connectAbility(want, connect) @@ -203,12 +232,13 @@ import featureAbility from "@ohos.ability.featureAbility" deviceManager.createDeviceManager("ohos.rpc.test", deviceManagerCallback) ``` + +3. 服务端处理客户端请求 -3. 服务端被绑定的Ability在onConnect方法里返回继承自rpc.RemoteObject的对象,该对象需要实现onRemoteMessageRequest方法,处理客户端的请求。 + 服务端被绑定的Ability在onConnect方法里返回继承自rpc.RemoteObject的对象,该对象需要实现onRemoteMessageRequest方法,处理客户端的请求。 - ``` - import rpc from "@ohos.rpc" + ```ts onConnect(want: Want) { var robj:rpc.RemoteObject = new Stub("rpcTestAbility") return robj @@ -224,12 +254,13 @@ import featureAbility from "@ohos.ability.featureAbility" } ``` + +4. 客户端处理服务端响应 -4. 客户端在onConnect回调里接收到代理对象,调用sendRequestAsync方法发起请求,在期约或者回调函数里接收结果。 + 客户端在onConnect回调里接收到代理对象,调用sendRequestAsync方法发起请求,在期约(JavaScript期约:用于表示一个异步操作的最终完成或失败及其结果值)或者回调函数里接收结果。 - ``` - import rpc from "@ohos.rpc" + ```ts // 使用期约 let option = new rpc.MessageOption() let data = rpc.MessageParcel.create() @@ -271,11 +302,11 @@ import featureAbility from "@ohos.ability.featureAbility" proxy.sendRequest(1, data, reply, option, sendRequestCallback) ``` +5. 断开连接 + IPC通信结束后,使用featureAbility的接口断开连接。 -5. IPC通信结束后,使用featureAbility的接口断开连接。 - - ``` + ```ts import rpc from "@ohos.rpc" import featureAbility from "@ohos.ability.featureAbility" function disconnectCallback() { diff --git a/zh-cn/application-dev/connectivity/ipc-rpc-overview.md b/zh-cn/application-dev/connectivity/ipc-rpc-overview.md index f081a9ca13f54a705f51579440bb8e3cf2db7366..5dbf04f2d32e2ee7bca088cbd8f0a434d0e277a3 100755 --- a/zh-cn/application-dev/connectivity/ipc-rpc-overview.md +++ b/zh-cn/application-dev/connectivity/ipc-rpc-overview.md @@ -5,15 +5,19 @@ IPC(Inter-Process Communication)与RPC(Remote Procedure Call)用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,后者使用软总线驱动,用于跨设备跨进程通信。需要跨进程通信的原因是因为每个进程都有自己独立的资源和内存空间,其他进程不能随意访问不同进程的内存和资源,IPC/RPC便是为了突破这一点。IPC和RPC通常采用客户端-服务器(Client-Server)模型,在使用时,请求服务的(Client)一端进程可获取提供服务(Server)一端所在进程的代理(Proxy),并通过此代理读写数据来实现进程间的数据通信,更具体的讲,首先请求服务的(Client)一端会建立一个服务提供端(Server)的代理对象,这个代理对象具备和服务提供端(Server)一样的功能,若想访问服务提供端(Server)中的某一个方法,只需访问代理对象中对应的方法即可,代理对象会将请求发送给服务提供端(Server);然后服务提供端(Server)处理接受到的请求,处理完之后通过驱动返回处理结果给代理对象;最后代理对象将请求结果进一步返回给请求服务端(Client)。通常,Server会先注册系统能力(System Ability)到系统能力管理者(System Ability Manager,缩写SAMgr)中,SAMgr负责管理这些SA并向Client提供相关的接口。Client要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理,然后使用代理和SA通信。下文直接使用Proxy表示服务请求方,Stub表示服务提供方。 +![IPC&RPC通信机制](figures/075sd302-aeb9-481a-bb8f-e552sdb61ead.PNG) + ## 约束与限制 -单个设备上跨进程通信时,传输的数据量最大约为1MB,过大的数据量请使用匿名共享内存。不支持的第一个场景是在RPC中订阅匿名Stub对象的死亡通知。 -不支持把跨设备的Proxy对象传递回该Proxy对象所指向的Stub对象所在的设备,即指向远端A侧Stub的Proxy对象不能在本设备内进行二次跨进程传递。 +- 单个设备上跨进程通信时,传输的数据量最大约为1MB,过大的数据量请使用[匿名共享内存](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-rpc.md#ashmem8) + +- 不支持在RPC中订阅匿名Stub对象(没有向SAMgr注册Stub对象)的死亡通知。 +- 不支持把跨设备的Proxy对象传递回该Proxy对象所指向的Stub对象所在的设备,即指向远端设备Stub的Proxy对象不能在本设备内进行二次跨进程传递。 ## 使用建议 -首先,需要编写接口类,接口类中必须定义消息码,供通信双方标识操作,还可以有未实现的的方法,还需要实现AsObject方法及OnRemoteRequest方法,必然地,也需要编写Proxy端,实现接口类中的方法和AsObject方法,也可以封装一些额外的方法用于调用SendRequest向对端发送数据。以上三者都具备后,便可以向SAMgr注册SA了,此时的注册应该在Stub所在进程完成。最后,在需要的地方从SAMgr中获取Proxy,便可通过Proxy实现与Stub的跨进程通信了。 +首先,需要编写接口类,接口类中必须定义消息码,供通信双方标识操作,可以有未实现的的方法,因为通信双方均需继承该接口类且双方不能是抽象类,所以此时定义的未实现的方法必须在双方继承时给出实现,这保证了继承双方不是抽象类。然后,需要编写Stub端相关类及其接口,并且实现AsObject方法及OnRemoteRequest方法。同时,也需要编写Proxy端,实现接口类中的方法和AsObject方法,也可以封装一些额外的方法用于调用SendRequest向对端发送数据。以上三者都具备后,便可以向SAMgr注册SA了,此时的注册应该在Stub所在进程完成。最后,在需要的地方从SAMgr中获取Proxy,便可通过Proxy实现与Stub的跨进程通信了。 相关步骤: diff --git a/zh-cn/application-dev/connectivity/subscribe-remote-state.md b/zh-cn/application-dev/connectivity/subscribe-remote-state.md index 76b010f000ea3d580c901998cc802ebf7d8f11a2..7ce385d8e16ec68f5eaaef0bad7ca81770f2595f 100755 --- a/zh-cn/application-dev/connectivity/subscribe-remote-state.md +++ b/zh-cn/application-dev/connectivity/subscribe-remote-state.md @@ -1,10 +1,10 @@ # 远端状态订阅开发实例 -IPC/RPC提供对远端Stub对象状态的订阅机制, 在远端Stub对象死亡时,可触发死亡通知告诉本地Proxy对象。这种状态通知订阅需要调用特定接口完成,当不再需要订阅时也需要调用特定接口取消。使用这种订阅机制的用户,需要实现死亡通知接口DeathRecipient并实现onRemoteDied方法清理资源。该方法会在远端Stub对象所在进程死亡或所在设备离开组网时被回调。值得注意的是,调用这些接口有一定的顺序。首先,需要Proxy订阅Stub死亡通知,若在订阅期间Stub状态正常,则在不再需要时取消订阅;若在订阅期间Stub所在进程退出或者所在设备退出组网,则会自动触发Proxy自定义的后续操作。 +IPC/RPC提供对远端Stub对象状态的订阅机制, 在远端Stub对象消亡时,可触发消亡通知告诉本地Proxy对象。这种状态通知订阅需要调用特定接口完成,当不再需要订阅时也需要调用特定接口取消。使用这种订阅机制的用户,需要实现消亡通知接口DeathRecipient并实现onRemoteDied方法清理资源。该方法会在远端Stub对象所在进程消亡或所在设备离开组网时被回调。值得注意的是,调用这些接口有一定的顺序。首先,需要Proxy订阅Stub消亡通知,若在订阅期间Stub状态正常,则在不再需要时取消订阅;若在订阅期间Stub所在进程退出或者所在设备退出组网,则会自动触发Proxy自定义的后续操作。 ## 使用场景 -这种订阅机制适用于本地Proxy对象需要感知远端Stub对象所在进程死亡,或所在设备离开组网的场景。当Proxy感知到Stub端死亡后,可适当清理本地资源。此外,RPC目前不提供匿名Stub对象的死亡通知,即只有向SAMgr注册过的服务才能被订阅死亡通知,IPC则支持匿名对象的死亡通知。 +这种订阅机制适用于本地Proxy对象需要感知远端Stub对象所在进程消亡,或所在设备离开组网的场景。当Proxy感知到Stub端消亡后,可适当清理本地资源。此外,RPC目前不提供匿名Stub对象的消亡通知,即只有向SAMgr注册过的服务才能被订阅消亡通知,IPC则支持匿名对象的消亡通知。 ## Native侧接口 @@ -18,12 +18,18 @@ IPC/RPC提供对远端Stub对象状态的订阅机制, 在远端Stub对象死 ### 参考代码 ```C++ +#include "iremote_broker.h" +#include "iremote_stub.h" + + //定义消息码 enum { TRANS_ID_PING_ABILITY = 5, TRANS_ID_REVERSED_MONITOR }; +const std::string DESCRIPTOR = "test.ITestAbility"; + class ITestService : public IRemoteBroker { public: // DECLARE_INTERFACE_DESCRIPTOR是必需的,入参需使用std::u16string; @@ -58,7 +64,9 @@ int TestServiceProxy::TestPingAbility(const std::u16string &dummy){ -``` +```c++ +#include "iremote_object.h" + class TestDeathRecipient : public IRemoteObject::DeathRecipient { public: virtual void OnRemoteDied(const wptr& remoteObject); @@ -71,23 +79,62 @@ void TestDeathRecipient::OnRemoteDied(const wptr& remoteObject) ```c++ sptr object = new IPCObjectProxy(1, to_utf16(DESCRIPTOR)); -sptr deathRecipient (new TestDeathRecipient());// 构造一个死亡通知对象 -bool result = object->AddDeathRecipient(deathRecipient); // 注册死亡通知 -result = object->RemoveDeathRecipient(deathRecipient); // 移除死亡通知 +sptr deathRecipient (new TestDeathRecipient());// 构造一个消亡通知对象 +bool result = object->AddDeathRecipient(deathRecipient); // 注册消亡通知 +result = object->RemoveDeathRecipient(deathRecipient); // 移除消亡通知 ``` +## JS侧接口 +| 接口名 | 返回值类型 | 功能描述 | +| -------------------- | ---------- | ------------------------------------------------------------ | +| addDeathRecippient | boolean | 注册用于接收远程对象消亡通知的回调,增加proxy对象上的消亡通知。 | +| removeDeathRecipient | boolean | 注销用于接收远程对象消亡通知的回调。 | +| onRemoteDied | void | 在成功添加死亡通知订阅后,当远端对象死亡时,将自动调用本方法。 | -## Stub感知Proxy死亡(匿名Stub的使用) +### 参考代码 -正向的死亡通知是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,便实现了反向死亡通知。 +```ts +import FA from "@ohos.ability.featureAbility"; +let proxy; +let connect = { + onConnect: function(elementName, remoteProxy) { + console.log("RpcClient: js onConnect called."); + proxy = remoteProxy; + }, + onDisconnect: function(elementName) { + console.log("RpcClient: onDisconnect"); + }, + onFailed: function() { + console.log("RpcClient: onFailed"); + } +}; +let want = { + "bundleName": "com.ohos.server", + "abilityName": "com.ohos.server.MainAbility", +}; +FA.connectAbility(want, connect); +class MyDeathRecipient { + onRemoteDied() { + console.log("server died"); + } +} +let deathRecipient = new MyDeathRecipient(); +proxy.addDeathRecippient(deathRecipient, 0); +proxy.removeDeathRecipient(deathRecipient, 0); +``` + +## Stub感知Proxy消亡(匿名Stub的使用) + +正向的消亡通知是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,便实现了反向消亡通知。 注意: -- 反向死亡通知仅限设备内跨进程通信使用,不可用于跨设备 -- 当匿名Stub对象没有被任何一个Proxy指向的时候,内核会自动回收 +> 反向死亡通知仅限设备内跨进程通信使用,不可用于跨设备。 -### Native侧参考代码 +> 当匿名Stub对象没有被任何一个Proxy指向的时候,内核会自动回收。 + +### 参考代码 ```c++ //Proxy