欢迎光临
我们一直在努力

鸿蒙 跨进程通信:IPC与RPC

本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

  鸿蒙开发中,IPC(Inter-Process Communication,进程间通信) 和 RPC(Remote Procedure Call,远程过程调用) 是跨进程通信的核心机制。

术语说明
IPC 同一设备内不同进程之间的通信
RPC 跨设备的不同进程之间的通信(远程调用)
Proxy 客户端进程中的代理对象,用于调用服务端接口
Stub 服务端进程中的存根对象,用于处理客户端请求

IPC/RPC的主要工作是跨进程建立对象通信的连接,使客户端进程的Proxy和服务端进程的Stub建立一一对应关系,从而通过Proxy的接口可以和Stub进行IPC/RPC通信。

二、开发准备:导入所需模块

在进行IPC/RPC通信开发前,需要导入相关模块:

// 基础模块
import { Want, common } from '@kit.AbilityKit'; // Ability相关
import { rpc } from '@kit.IPCKit'; // IPC/RPC核心模块
import { hilog } from '@kit.PerformanceAnalysisKit'; // 日志打印
import { BusinessError } from '@kit.BasicServicesKit'; // 错误处理

// RPC场景额外需要的模块(跨设备通信)
import { distributedDeviceManager } from '@kit.DistributedServiceKit';

三、客户端开发步骤

3.1 创建Want和Connect

IPC场景(同设备通信)

// 定义变量
let proxy: rpc.IRemoteObject | undefined;
let want: Want = {
// 包名和组件名写实际的值
bundleName: "ohos.rpc.test.server", // 服务端应用包名
abilityName: "ohos.rpc.test.server.ServiceAbility", // 服务端Ability名称
};

// 创建连接选项
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');
}
};

RPC场景(跨设备通信)

let dmInstance: distributedDeviceManager.DeviceManager | undefined;
let proxy: rpc.IRemoteObject | undefined;
let deviceList: Array<distributedDeviceManager.DeviceBasicInfo> | undefined;
let networkId: string | undefined;
let want: Want | undefined;
let connect: common.ConnectOptions | undefined;

// 1. 创建设备管理器
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);
}

// 2. 获取目标设备NetworkId
if (dmInstance != undefined) {
try {
deviceList = dmInstance.getAvailableDeviceListSync();
if (deviceList.length !== 0) {
networkId = deviceList[0].networkId; // 获取第一个可用设备的NetworkId

// 3. 创建Want,包含deviceId
want = {
bundleName: "ohos.rpc.test.server",
abilityName: "ohos.rpc.test.service.ServiceAbility",
deviceId: networkId, // 指定目标设备
};

// 4. 创建连接选项
connect = {
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');
}
};
}
} catch (error) {
let err: BusinessError = error as BusinessError;
hilog.error(0x0000, 'testTag', 'createDeviceManager err:' + err);
}
}

3.2 连接服务

FA模型(Feature Ability)

import { featureAbility } from '@kit.AbilityKit';

// 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
let connectId = featureAbility.connectAbility(want, connect);

Stage模型

// 获取UIAbilityContext
let context: common.UIAbilityContext = this.getUIContext().getHostContext();

// 连接ServiceExtensionAbility
let connectId = context.connectServiceExtensionAbility(want, connect);

注意事项:

  • connectId必须保存,断开连接时需要用到

  • 在Stage模型中,需要通过getUIContext().getHostContext()获取UIAbilityContext

3.3 发送消息到服务端

成功连接服务后,可以通过onConnect回调函数获取服务端的代理对象Proxy,然后使用该Proxy调用sendMessageRequest方法发起请求。

import { rpc } from '@kit.IPCKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

// 此示例代码段中的proxy是在与服务端连接成功后的onConnect回调里拿到的proxy
let proxy: rpc.IRemoteObject | undefined;

// 使用Promise契约发送请求
let option = new rpc.MessageOption(); // 消息选项
let data = rpc.MessageSequence.create(); // 发送数据序列
let reply = rpc.MessageSequence.create(); // 接收数据序列

// 在data里写入参数,以传递字符串为例
data.writeString("hello world");

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里读取结果
// 此处是根据前面创建ServiceExtensionAbility,实现服务端做的示例
let response = result.reply.readString();
hilog.info(0x0000, 'testTag', '收到服务端响应: ' + response);
})
.catch((e: Error) => {
hilog.error(0x0000, 'testTag', 'sendMessageRequest got exception: ' + e);
})
.finally(() => {
// 释放资源
data.reclaim();
reply.reclaim();
});
}

sendMessageRequest参数:

参数类型说明
code number 请求码,用于区分不同的请求类型
data MessageSequence 发送给服务端的数据
reply MessageSequence 接收服务端返回的数据
option MessageOption 消息选项(如同步/异步)

MessageSequence常用方法:

方法说明
writeString(val: string) 写入字符串
writeInt(val: number) 写入整型
writeBoolean(val: boolean) 写入布尔值
writeParcelable(val: Object) 写入可序列化对象
readString() 读取字符串
readInt() 读取整型
reclaim() 释放资源

四、服务端开发步骤

4.1 创建Stub对象

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

import { rpc } from '@kit.IPCKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

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> {

// 服务端Stub根据不同的请求code分别执行对应的处理流程
if (code == 1) {
// 读取客户端发送的数据
let str = data.readString();
hilog.info(0x0000, 'testTag', 'stub receive str : ' + str);

// 服务端使用reply回传请求处理的结果给客户端
reply.writeString("hello rpc");

return true; // 处理成功
} else {
hilog.info(0x0000, 'testTag', 'stub unknown code: ' + code);
return false; // 未知请求码
}
}
}

4.2 在ServiceExtensionAbility中返回Stub

// ServiceAbility.ts
import { ServiceExtensionAbility, Want } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';

export default class ServiceAbility extends ServiceExtensionAbility {

// 当客户端连接时调用,需要返回Stub对象
onConnect(want: Want): rpc.RemoteObject {
return new Stub("service stub");
}

// 其他生命周期方法…
}

Stub对象说明:

  • onRemoteMessageRequest是处理客户端请求的核心方法

  • code参数用于区分不同的业务请求

  • 必须通过reply返回处理结果给客户端

  • 返回true表示处理成功,false表示失败

五、断开连接

通信结束后,需要断开与服务的连接,释放资源。

FA模型

import { featureAbility } from "@kit.AbilityKit";
import { hilog } from '@kit.PerformanceAnalysisKit';

function disconnectCallback() {
hilog.info(0x0000, 'testTag', 'disconnect ability done');
}

// 断开连接,使用连接服务成功时保存下来的connectId断开连接
featureAbility.disconnectAbility(connectId, disconnectCallback);

Stage模型

// 获取UIAbilityContext
let context: common.UIAbilityContext = this.getUIContext().getHostContext();

// 断开连接,使用连接服务成功时保存下来的connectId断开连接
context.disconnectServiceExtensionAbility(connectId);

注意事项

线程模型

  • IPC/RPC调用默认是同步的,可通过MessageOption设置为异步

  • 服务端的onRemoteMessageRequest运行在IPC线程,不应执行耗时操作

  • 确保bundleName和abilityName正确无误

  • RPC场景需要正确获取目标设备的NetworkId

  • 跨设备通信需要确保设备在同一个组网内

数据序列化

支持的数据类型:

  • 基本类型:string、number、boolean

  • 实现了Parcelable接口的自定义对象

  • 数组和List集合

赞(0)
未经允许不得转载:171主机测评 » 鸿蒙 跨进程通信:IPC与RPC
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址