基本概念
IPC(Inter-Process Communication)与RPC(Remote Procedure Call)用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,后者使用软总线驱动,用于跨设备跨进程通信。需要跨进程通信的原因是因为每个进程都有自己独立的资源和内存空间,其他进程不能随意访问不同进程的内存和资源,IPC/RPC便是为了突破这一点。
说明
Stage模型不能直接使用本文介绍的IPC和RPC,需要通过以下能力实现相关业务场景:
- IPC典型使用场景在后台服务,应用的后台服务通过IPC机制提供跨进程的服务调用能力。
- RPC典型使用场景在多端协同,多端协同通过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对象不能在本设备内进行二次跨进程传递。
IPC与RPC通信开发指南
场景介绍
IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,包括Proxy和Stub运行在不同设备的情况。
开发步骤
ArkTS侧开发步骤
说明
- 此文档中的示例代码描述的是系统应用跨进程通信。
- 当前不支持三方应用实现ServiceExtensionAbility,三方应用的UIAbility组件可以通过Context连接系统提供的ServiceExtensionAbility。
- 当前使用场景: 仅限客户端是三方应用,服务端是系统应用。
- 添加依赖
// FA模型需要从@kit.AbilityKit导入featureAbility
// import { featureAbility } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';
- 绑定Ability
首先,构造变量want,指定要绑定的Ability所在应用的包名、组件名,如果是跨设备的场景,还需要绑定目标设备NetworkId(组网场景下对应设备的标识符,可以使用distributedDeviceManager获取目标设备的NetworkId);然后,构造变量connect,指定绑定成功、绑定失败、断开连接时的回调函数;最后,FA模型使用featureAbility提供的接口绑定Ability,Stage模型通过context获取服务后用提供的接口绑定Ability。
// FA模型需要从@kit.AbilityKit导入featureAbility
// import { featureAbility } from "@kit.AbilityKit";
import { Want, common } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { distributedDeviceManager } from '@kit.DistributedServiceKit';
import { BusinessError } from '@kit.BasicServicesKit';
let dmInstance: distributedDeviceManager.DeviceManager | undefined;
let proxy: rpc.IRemoteObject | undefined;
let connectId: number;
// 单个设备绑定Ability
let want: Want = {
// 包名和组件名写实际的值
bundleName: "ohos.rpc.test.server",
abilityName: "ohos.rpc.test.server.ServiceAbility",
};
let connect: common.ConnectOptions = {
onConnect: (elementName, remoteProxy) => {
hilog.info(0x0000, 'testTag', 'RpcClient: js onConnect called');
proxy = remoteProxy;
},
onDisconnect: (elementName) => {
hilog.info(0x0000, 'testTag', 'RpcClient: onDisconnect');
},
onFailed: () => {
hilog.info(0x0000, 'testTag', 'RpcClient: onFailed');
}
};
// FA模型使用此方法连接服务
// connectId = featureAbility.connectAbility(want, connect);
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
// 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
connectId = context.connectServiceExtensionAbility(want,connect);
// 跨设备绑定
try{
dmInstance = distributedDeviceManager.createDeviceManager("ohos.rpc.test");
} catch(error) {
let err: BusinessError = error as BusinessError;
hilog.error(0x0000, 'testTag', 'createDeviceManager errCode:' + err.code + ', errMessage:' + err.message);
}
// 使用distributedDeviceManager获取目标设备NetworkId
if (dmInstance != undefined) {
let deviceList = dmInstance.getAvailableDeviceListSync();
let networkId = deviceList[0].networkId;
let want: Want = {
bundleName: "ohos.rpc.test.server",
abilityName: "ohos.rpc.test.service.ServiceAbility",
deviceId: networkId,
flags: 256
};
// 建立连接后返回的Id需要保存下来,在断开连接时需要作为参数传入
// FA模型使用此方法连接服务
// connectId = featureAbility.connectAbility(want, connect);
// 第一个参数是本应用的包名,第二个参数是接收distributedDeviceManager的回调函数
connectId = context.connectServiceExtensionAbility(want,connect);
}
- 服务端处理客户端请求
服务端被绑定的Ability在onConnect方法里返回继承自 rpc.RemoteObject 的对象,该对象需要实现 onRemoteMessageRequest 方法,处理客户端的请求。
import { rpc } from '@kit.IPCKit';
import { Want } from '@kit.AbilityKit';
class Stub extends rpc.RemoteObject {
constructor(descriptor: string) {
super(descriptor);
}
onRemoteMessageRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence, option: rpc.MessageOption): boolean | Promise<boolean> {
// 根据code处理客户端的请求
return true;
}
onConnect(want: Want) {
const robj: rpc.RemoteObject = new Stub("rpcTestAbility");
return robj;
}
}
- 客户端处理服务端响应
客户端在onConnect回调里接收到代理对象,调用 sendMessageRequest 方法发起请求,在期约(用于表示一个异步操作的最终完成或失败及其结果值)或者回调函数里接收结果。
import { rpc } from '@kit.IPCKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
// 使用期约
let option = new rpc.MessageOption();
let data = rpc.MessageSequence.create();
let reply = rpc.MessageSequence.create();
// 往data里写入参数
let proxy: rpc.IRemoteObject | undefined;
if (proxy != undefined) {
proxy.sendMessageRequest(1, data, reply, option)
.then((result: rpc.RequestResult) => {
if (result.errCode != 0) {
hilog.error(0x0000, 'testTag', 'sendMessageRequest failed, errCode: ' + result.errCode);
return;
}
// 从result.reply里读取结果
})
.catch((e: Error) => {
hilog.error(0x0000, 'testTag', 'sendMessageRequest got exception: ' + e);
})
.finally(() => {
data.reclaim();
reply.reclaim();
})
}
// 使用回调函数
function sendRequestCallback(err: Error, result: rpc.RequestResult) {
try {
if (result.errCode != 0) {
hilog.error(0x0000, 'testTag', 'sendMessageRequest failed, errCode: ' + result.errCode);
return;
}
// 从result.reply里读取