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

  鸿蒙开发中,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集合

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐