一、IPC与RPC通信概述

基本概念

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表示服务提供方。

约束与限制

● 单个设备上跨进程通信时,传输的数据量最大约为1MB,过大的数据量请使用匿名共享内存

● 不支持在RPC中订阅匿名Stub对象(没有向SAMgr注册Stub对象)的死亡通知。

● 不支持把跨设备的Proxy对象传递回该Proxy对象所指向的Stub对象所在的设备,即指向远端设备Stub的Proxy对象不能在本设备内进行二次跨进程传递。

使用建议

首先,需要编写接口类,接口类中必须定义消息码,供通信双方标识操作,可以有未实现的的方法,因为通信双方均需继承该接口类且双方不能是抽象类,所以此时定义的未实现的方法必须在双方继承时给出实现,这保证了继承双方不是抽象类。然后,需要编写Stub端相关类及其接口,并且实现AsObject方法及OnRemoteRequest方法。同时,也需要编写Proxy端,实现接口类中的方法和AsObject方法,也可以封装一些额外的方法用于调用SendRequest向对端发送数据。以上三者都具备后,便可以向SAMgr注册SA了,此时的注册应该在Stub所在进程完成。最后,在需要的地方从SAMgr中获取Proxy,便可通过Proxy实现与Stub的跨进程通信了。

相关步骤:

● 实现接口类:需继承IRemoteBroker,需定义消息码,可声明不在此类实现的方法。

● 实现服务提供端(Stub):需继承IRemoteStub或者RemoteObject,需重写AsObject方法及OnRemoteRequest方法。

● 实现服务请求端(Proxy):需继承IRemoteProxy或RemoteProxy,需重写AsObject方法,封装所需方法调用SendRequest。

● 注册SA:申请SA的唯一ID,向SAMgr注册SA。

● 获取SA:通过SA的ID和设备ID获取Proxy,使用Proxy与远端通信

二、  IPC与RPC通信开发指导

场景介绍

IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,包括Proxy和Stub运行在不同设备的情况。

接口说明

表1 Native侧IPC接口

类/接口

方法

功能说明

IRemoteBroker

sptr<IRemoteObject> AsObject()

返回通信对象。Stub端返回RemoteObject对象本身,Proxy端返回代理对象。

IRemoteStub

virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)

请求处理方法,派生类需要重写该方法用来处理Proxy的请求并返回结果。

IRemoteProxy

Remote()->SendRequest(code, data, reply, option)

消息发送方法,业务的Proxy类需要从IRemoteProxy类派生,该方法用来向对端发送消息。

开发步骤

Native侧开发步骤

1.  添加依赖

SDK依赖:

#ipc场景
external_deps = [
"ipc:ipc_single",
] #rpc场景
external_deps = [
"ipc:ipc_core",
]

  

此外, IPC/RPC依赖的refbase实现在公共基础库下,请增加对utils的依赖:

external_deps = [
"c_utils:utils",
]

  

2.定义IPC接口ITestAbility

SA接口继承IPC基类接口IRemoteBroker,接口里定义描述符、业务函数和消息码,其中业务函数在Proxy端和Stub端都需要实现。

#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(to_utf16(DESCRIPTOR));
virtual int TestPingAbility(const std::u16string &dummy) = 0; // 定义业务函数
};

  

3.定义和实现服务端TestAbilityStub

该类是和IPC框架相关的实现,需要继承 IRemoteStub<ITestAbility>。Stub端作为接收请求的一端,需重写OnRemoteRequest方法用于接收客户端调用。

#include "iability_test.h"
#include "iremote_stub.h" class TestAbilityStub : public IRemoteStub<ITestAbility> {
public:
virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override;
int TestPingAbility(const std::u16string &dummy) override;
}; int TestAbilityStub::OnRemoteRequest(uint32_t code,
MessageParcel &data, MessageParcel &reply, MessageOption &option)
{
switch (code) {
case TRANS_ID_PING_ABILITY: {
std::u16string dummy = data.ReadString16();
int result = TestPingAbility(dummy);
reply.WriteInt32(result);
return 0;
}
default:
return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
}
}

  

4.  定义服务端业务函数具体实现类TestAbility

#include "iability_server_test.h"

class TestAbility : public TestAbilityStub {
public:
int TestPingAbility(const std::u16string &dummy);
} int TestAbility::TestPingAbility(const std::u16string &dummy) {
return 0;
}

  

5.  定义和实现客户端 TestAbilityProxy

该类是Proxy端实现,继承IRemoteProxy<ITestAbility>,调用SendRequest接口向Stub端发送请求,对外暴露服务端提供的能力。

#include "iability_test.h"
#include "iremote_proxy.h"
#include "iremote_object.h" class TestAbilityProxy : public IRemoteProxy<ITestAbility> {
public:
explicit TestAbilityProxy(const sptr<IRemoteObject> &impl);
int TestPingAbility(const std::u16string &dummy) override;
private:
static inline BrokerDelegator<TestAbilityProxy> delegator_; // 方便后续使用iface_cast宏
} TestAbilityProxy::TestAbilityProxy(const sptr<IRemoteObject> &impl)
: IRemoteProxy<ITestAbility>(impl)
{
} int TestAbilityProxy::TestPingAbility(const std::u16string &dummy){
MessageOption option;
MessageParcel dataParcel, replyParcel;
dataParcel.WriteString16(dummy);
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,设备内与分布式的注册参数不同。

// 注册到本设备内
auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
samgr->AddSystemAbility(saId, new TestAbility()); // 在组网场景下,会被同步到其他设备上
auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
ISystemAbilityManager::SAExtraProp saExtra;
saExtra.isDistributed = true; // 设置为分布式SA
int result = samgr->AddSystemAbility(saId, new TestAbility(), saExtra);

  

7.  SA获取与调用

通过SystemAbilityManager的GetSystemAbility方法可获取到对应SA的代理IRemoteObject,然后构造TestAbilityProxy即可。

// 获取本设备内注册的SA的proxy
sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(saId);
sptr<ITestAbility> testAbility = iface_cast<ITestAbility>(remoteObject); // 使用iface_cast宏转换成具体类型 // 获取其他设备注册的SA的proxy
sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); // networkId是组网场景下对应设备的标识符,可以通过GetLocalNodeDeviceInfo获取
sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(saId, networkId);
sptr<TestAbilityProxy> proxy(new TestAbilityProxy(remoteObject)); // 直接构造具体Proxy

  

JS侧开发步骤

1.  添加依赖

import rpc from "@ohos.rpc"
import featureAbility from "@ohos.ability.featureAbility"

  

2.绑定Ability

首先,构造变量want,指定要绑定的Ability所在应用的包名、组件名,如果是跨设备的场景,还需要绑定目标设备NetworkId(组网场景下对应设备的标识符,可以使用deviceManager获取目标设备的NetworkId);然后,构造变量connect,指定绑定成功、绑定失败、断开连接时的回调函数;最后,使用featureAbility提供的接口绑定Ability。

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",
"abilityName": "ohos.rpc.test.server.ServiceAbility",
}
let connect = {
onConnect:function(elementName, remote) {
proxy = remote
},
onDisconnect:function(elementName) {
},
onFailed:function() {
proxy = null
}
}
connectId = featureAbility.connectAbility(want, connect) // 如果是跨设备绑定,可以使用deviceManager获取目标设备NetworkId
import deviceManager from '@ohos.distributedHardware.deviceManager'
function deviceManagerCallback(deviceManager) {
let deviceList = deviceManager.getTrustedDeviceListSync()
let networkId = deviceList[0].networkId
let want = {
"bundleName": "ohos.rpc.test.server",
"abilityName": "ohos.rpc.test.service.ServiceAbility",
"networkId": networkId,
"flags": 256
}
connectId = featureAbility.connectAbility(want, connect)
}
// 第一个参数是本应用的包名,第二个参数是接收deviceManager的回调函数
deviceManager.createDeviceManager("ohos.rpc.test", deviceManagerCallback)

  

3.服务端处理客户端请求

服务端被绑定的Ability在onConnect方法里返回继承自rpc.RemoteObject的对象,该对象需要实现onRemoteMessageRequest方法,处理客户端的请求。

onConnect(want: Want) {
var robj:rpc.RemoteObject = new Stub("rpcTestAbility")
return robj
}
class Stub extends rpc.RemoteObject {
constructor(descriptor) {
super(descriptor)
}
onRemoteMessageRequest(code, data, reply, option) {
// 根据code处理客户端的请求
return true
}
}

  

4.客户端处理服务端响应

客户端在onConnect回调里接收到代理对象,调用sendRequestAsync方法发起请求,在期约(JavaScript期约:用于表示一个异步操作的最终完成或失败及其结果值)或者回调函数里接收结果。

// 使用期约
let option = new rpc.MessageOption()
let data = rpc.MessageParcel.create()
let reply = rpc.MessageParcel.create()
// 往data里写入参数
proxy.sendRequestAsync(1, data, reply, option)
.then(function(result) {
if (result.errCode != 0) {
console.error("send request failed, errCode: " + result.errCode)
return
}
// 从result.reply里读取结果
})
.catch(function(e) {
console.error("send request got exception: " + e)
}
.finally(() => {
data.reclaim()
reply.reclaim()
}) // 使用回调函数
function sendRequestCallback(result) {
try {
if (result.errCode != 0) {
console.error("send request failed, errCode: " + result.errCode)
return
}
// 从result.reply里读取结果
} finally {
result.data.reclaim()
result.reply.reclaim()
}
}
let option = new rpc.MessageOption()
let data = rpc.MessageParcel.create()
let reply = rpc.MessageParcel.create()
// 往data里写入参数
proxy.sendRequest(1, data, reply, option, sendRequestCallback)

  

5.断开连接

IPC通信结束后,使用featureAbility的接口断开连接。

import rpc from "@ohos.rpc"
import featureAbility from "@ohos.ability.featureAbility"
function disconnectCallback() {
    console.info("disconnect ability done")
}
featureAbility.disconnectAbility(connectId, disconnectCallback)

HarmonyOS跨进程通信—IPC与RPC通信开发指导的更多相关文章

  1. 【Android跨进程】IPC总结

    前言 IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程.两个进程可以是两个独立的app也可以是一个app的两个 ...

  2. 【Chromium中文文档】跨进程通信 (IPC)

    跨进程通信 (IPC) 转载请注明出处:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//General_Architecture/I ...

  3. android跨进程通信(IPC)——AIDL

    转载请标明出处: http://blog.csdn.net/sinat_15877283/article/details/51026711: 本文出自: [温利东的博客] 近期在看 @任玉刚 大神编写 ...

  4. Service官方教程(11)Bound Service示例之2-AIDL 定义跨进程接口并通信

    Android Interface Definition Language (AIDL) 1.In this document Defining an AIDL Interface Create th ...

  5. 跨进程通信之Messenger

    1.简介 Messenger,顾名思义即为信使,通过它可以在不同进程中传递Message对象,通过在Message中放入我们需要的入局,就可以轻松实现数据的跨进程传递了.Messenger是一种轻量级 ...

  6. 【朝花夕拾】Android性能篇之(七)Android跨进程通信篇

    前言 只要是面试高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点之一.Android系统的运行由大量相互独立的进程相互协助来完成的,所以Android进程间通信问题,是做好Andro ...

  7. Android-Messenger跨进程通信

    http://blog.csdn.net/lmj623565791/article/details/47017485 一.概述 我们可以在客户端发送一个Message给服务端,在服务端的handler ...

  8. 图文详解 Android Binder跨进程通信机制 原理

    图文详解 Android Binder跨进程通信机制 原理 目录 目录 1. Binder到底是什么? 中文即 粘合剂,意思为粘合了两个不同的进程 网上有很多对Binder的定义,但都说不清楚:Bin ...

  9. linux 单机跨进程通信

    一般来说通过网络通信(比如tcp,udp)或者共享内存的方式肯定可以实现跨进程通信,但现在这里要说的是比较偏但实用的几个方法:利用unix域通信(普通网络连接),利用unix域通信(socketpai ...

  10. android 远程Service以及AIDL的跨进程通信

    在Android中,Service是运行在主线程中的,如果在Service中处理一些耗时的操作,就会导致程序出现ANR. 但如果将本地的Service转换成一个远程的Service,就不会出现这样的问 ...

随机推荐

  1. 【Azure 应用服务】Python Function App重新部署后,出现 Azure Functions runtime is unreachable 错误

    问题描述 Python Function App重新部署后,出现 Azure Functions runtime is unreachable 错误 问题解答 在Function App的门户页面中, ...

  2. 【Azure Developer】解答《美丽的数学》一书中P120页的一道谜题:寻找第四个阶乘和数

    一道谜题 在观看<美丽的数学>一书中,在120页中有一道谜题: 数字145被称为一个阶乘和数, 因为它具有以下有趣的属性,如果我们将它的各位数字的阶乘相加,会得到该数字本身 1! +4! ...

  3. C++ //案列-员工分组 ( 容器存放,查找,打印,统计,宏定义 ,随机)

    //案列-员工分组//描述:公司招聘10个员工(ABCDEFGHIJ),10名指派员工进入公司,需要指派那个员工在那个部门工作//员工信息有:姓名 工资组成: 部门分为:策划 美术 研发//随机给10 ...

  4. 迈向现代化的.NET配置指北

    一.欢呼 .NET Standard 时代   我现在已不大提 .NET Core,对于我来说,未来的开发将是基于 .NET Standard,不仅仅是面向未来 ,也是面向过去:不只是 .NET Co ...

  5. Learning by teaching --- 费曼学习法

    世界上存在成千上万种学习法,如果上天只让我掌握一种,那一定就是"费曼学习法". 介绍 费曼学习法是由诺贝尔物理学奖获得者理查德·费曼提出的一种学习方法,其核心思想是将所学内容用自己 ...

  6. 表单验证 validate 两种 一种是callback配合外部变量,当同步用。第2中是 then async await 这种 真正$api也适用

    validateFormRealProcessLeft () { let ret = false this.$refs.xxx.validate((valid) => { if (valid) ...

  7. RabbitMq消息队列进一步认识

    参考:https://www.cnblogs.com/wanglijun/p/10896896.html(应用场景) https://blog.csdn.net/weixin_41588751/art ...

  8. JavaScript自定义响应式对象

    1. 引言 这里的响应式对象是指JavaScript中的变量与HTML中的内容相绑定,变量更新则内容更新,也叫数据绑定 此时不得不说MVVM架构,MVVM架构思想的实现步骤如下: 模型(Model): ...

  9. .Net 8.0 下的新RPC,IceRPC之接口定义语言 [Slice] VS [Protobuf]

    作者引言 很高兴啊,我们来到了接口定义语言(IDL)篇,RPC之基石,有了它,可以在各种各种语言中实现RPC通讯. Slice 和 Protobuf 是什么? IceRPC的核心是一个面向字节byte ...

  10. 如何使用LOTO示波器 绘制 频率响应特性曲线?

    在工作和项目中,经常会遇到一个功能电路模块对信号进行调理,或滤波,或放大,或衰减,或阻抗变换.这些功能电路模块可能是无源阻容的,也可能是有源的运放电路,也可能是更复杂的系统.但是它们对信号进行调理的最 ...