- 文档
- 知识库
- 教程
- OpenHarmony
【免费下载链接】CangjieCorpus
本仓库为仓颉编程语言语料库,聚焦 仓颉编程语言 核心知识体系,整合官方开发指南、API 文档、典型代码示例及语法规范等多维度资源,经结构化处理与专业标注,构建为支持 RAG(检索增强生成)技术 的高质量知识基座,适配智能编码助手、学习辅助工具等应用开发需求。 欢迎开发者基于本语料库构建仓颉语言智能应用,推动鸿蒙开发生态智能化升级!
导读
本篇技术指南基于 OpenHarmony 仓颉(Cangjie)应用开发文档中 IPC Kit 简介 一文,系统讲解设备内进程间通信(IPC)与跨设备远程调用(RPC)的核心概念、Client-Server 实现原理、典型使用场景与数据约束,并结合本仓库中 ohos.rpc API 参考 与 RPC 错误码 源码文档,补充Parcelable、MessageSequence、Ashmem三大核心类型的实战用法。读完本篇,你将理解 IPC 与 RPC 的本质区别、代理(Proxy)与存根(Stub)的通信链路,以及开发多进程、多端协同应用时必须遵守的容量与行为限制。
IPC 与 RPC 的基本概念
什么是 IPC 与 RPC
在 OpenHarmony 仓颉应用开发体系中,IPC与RPC是两套既有联系又有区别的跨进程通信机制:
| 概念 | 全称 | 适用范围 | 底层驱动 |
|---|---|---|---|
| IPC | Inter-Process Communication(进程间通信) | 设备内跨进程通信 | Binder 驱动 |
| RPC | Remote Procedure Call(远程过程调用) | 跨设备跨进程通信 | 软总线驱动 |
两者的共同目标都是实现跨进程通信,核心区别在于通信范围与底层承载方式:
- IPC使用 Binder 驱动,面向同一台设备内的不同进程;
- RPC使用软总线(SoftBus)驱动,面向不同设备之间的进程,使得远端设备上的服务可以像本地服务一样被调用。
为什么需要跨进程通信
操作系统为每个进程分配了独立的资源和内存空间,一个进程默认无法随意访问另一个进程的内存和资源。IPC/RPC 正是为了突破这一进程隔离限制而存在的机制——它允许一个进程安全地请求另一个进程(甚至是另一台设备上的进程)提供的服务,同时依然维持进程间内存隔离带来的稳定性与安全性。
典型使用场景与 Stage 模型限制
说明:
Stage 模型不能直接使用本文介绍的 IPC 和 RPC,以下为 IPC 与 RPC 的典型使用场景:
- IPC 典型使用场景:后台服务。应用的后台服务通过 IPC 机制提供跨进程的服务调用能力,例如系统级能力被其他应用进程远程调用。
- RPC 典型使用场景:多端协同。通过 RPC 机制提供远端接口调用与数据传递能力,例如手机与平板、手机与智慧屏之间的协同交互。
结合 IPC Kit 目录 可见,IPC Kit 当前标注为Beta 特性,在实际工程中接入时需要评估其成熟度与版本适配。
实现原理:Client-Server 模型与 Proxy/Stub 机制
通信双方的角色划分
IPC 与 RPC 通常采用客户端-服务端(Client-Server)模型,在 IPC Kit 的术语体系中:
说明:
Client:请求服务的一端,被称为客户端。
Server:提供服务的一端,被称为服务端。
在 IPC Kit 中也经常用Proxy表示服务请求方(客户端-Client),Stub表示服务提供方(服务端-Server),后续文档中对 Proxy 和 Stub 不再做过多描述。
也就是说:服务端进程中的服务对象实体称为 Stub,客户端进程持有的远端服务替身称为 Proxy。
一次完整通信的流程
从 IPC Kit 简介 的实现原理描述可以归纳出完整的调用链路:
- 建立代理:请求方 Client 进程先建立一个服务端的代理对象(Proxy),该代理对象具备与服务端 Stub 相同的接口能力。开发者若想调用服务端某个方法,只需调用代理对象上对应的方法即可。
- 请求转发:代理对象将请求发送给服务端进程,服务端处理接收到的请求。
- 结果回传:服务端处理完毕后,通过驱动将处理结果返回给代理对象。
- 结果交付:代理对象将请求结果进一步返回给客户端调用方。
对上层开发者而言,整个过程呈现出"本地方法调用"的外观,而跨进程的数据搬运、编解码与驱动交互都被透明地封装掉了。
系统能力注册与获取:SAMgr 的作用
在服务端侧,通常流程是:
- Stub 注册:Stub 先将自身注册为系统能力(System Ability,缩写 SA),注册到系统能力管理者(System Ability Manager,缩写 SAMgr)中。
- SAMgr 统一管理:SAMgr 负责管理这些 SA,并向 Client 提供相关的接口。
- Client 获取 Proxy:Client 要与某个具体的 SA 通信,必须先从 SAMgr 中获取该 SA 的代理 Proxy 对象,然后使用该 Proxy 对象与 SA 通信。
驱动选择:在整个通信过程中,如果使用 IPC 通信,则依赖Binder 驱动;如果使用 RPC 通信,则依赖软总线驱动。
从 API 文档看通信的数据载体
实现上述链路时,真正承载业务数据的是 ohos.rpc(RPC通信) 模块提供的三个核心类型。在仓颉代码中通过如下方式导入:
import kit.IPCKit.*Parcelable接口:在进程间通信期间,将类的对象写入MessageSequence并从MessageSequence中恢复它们;MessageSequence类:发送方使用其写方法将数据以特定格式写入对象,接收方使用其读方法读取,数据格式涵盖基础类型及数组、IPC 对象、接口描述符和自定义序列化对象;Ashmem类:匿名共享内存,用于突破 IPC 单次传输 200KB 的数据量上限。
数据序列化:Parcelable 与 MessageSequence 实战
实现 Parcelable 接口
在 IPC/RPC 通信中传递自定义对象,需要让该类型实现Parcelable接口,其声明如下(系统能力SystemCapability.Communication.IPC.Core,起始版本 22):
public interface Parcelable { func marshalling(dataOut: MessageSequence): Bool func unmarshalling(dataIn: MessageSequence): Bool }marshalling(dataOut):将此可序列对象封送到MessageSequence中,参数dataOut为待写入的MessageSequence对象,返回Bool表示封送是否成功;unmarshalling(dataIn):从MessageSequence中解封此可序列对象,返回Bool表示反序列化是否成功。
在 cj-apis-rpc.md 中给出了完整的可编译示例(示例注释// index.cj表示可在仓颉模板工程的index.cj文件中编译运行):
// index.cj import kit.IPCKit.* // 此处代码可添加在依赖项定义中 class MyParcelable <: Parcelable { var num: Int32 = 0 var str: String = '' init() {} init(num: Int32, str: String) { this.num = num this.str = str } public func marshalling(messageSequence: MessageSequence): Bool { messageSequence.writeInt(this.num) messageSequence.writeString(this.str) return true } public func unmarshalling(messageSequence: MessageSequence): Bool { this.num = messageSequence.readInt() this.str = messageSequence.readString() return true } } let parcelable = MyParcelable(1, "aaa") let parcelable2 = MyParcelable(2, "bbb") let parcelable3 = MyParcelable(3, "ccc") let data = MessageSequence.create() data.writeParcelableArray(parcelable,parcelable2,parcelable3) let ret: Array<Parcelable> = [MyParcelable(0, ""), MyParcelable(0, ""), MyParcelable(0, "")] data.readParcelableArray(ret)关键点:
writeInt/writeString与readInt/readString必须成对且顺序一致,否则读取端会因读写顺序不一致而抛异常(对应错误码 1900010);- 泛型方法
writeParcelable<T>(val: T) where T <: Parcelable用于写入单个自定义对象,readParcelable<T>(dataIn: T)需传入一个已构造的目标对象实例用于接收成员变量,见 读写 Parcelable 示例; writeParcelableArray<T>(parcelableArray: Array<T>)与readParcelableArray<T>(Array<T>)支持对象数组的批量封送/解封。
MessageSequence 的容量与位置控制
MessageSequence提供了丰富的读写与缓冲控制方法,常用能力归纳如下:
| 类别 | 方法(节选) | 说明 |
|---|---|---|
| 创建 | static func create() | 创建MessageSequence对象 |
| 容量 | getCapacity() | 获取当前对象容量(字节) |
| 原始数据容量 | getRawDataCapacity() | 返回可容纳的最大原始数据量,即128MB |
| 读写位置 | getReadPosition()/getWritePosition() | 获取当前读/写位置 |
| 读写空间 | getReadableBytes()/getWritableBytes()/getSize() | 获取可读字节、可写字节与数据大小 |
| 回退 | rewindRead(UInt32)/rewindWrite(UInt32) | 回退读/写位置 |
| 容量与大小 | setCapacity(UInt32)/setSize(UInt32) | 设置缓冲区容量与数据大小 |
| 基础类型写入 | writeBoolean/writeByte/writeChar/writeShort/writeInt/writeLong/writeFloat/writeDouble/writeString及对应writeXxxArray | 写入基础类型及数组 |
| 基础类型读取 | 对应的readBoolean/readByte/ ... /readStringArray | 按序读取 |
| 自定义对象 | writeParcelable<T>/readParcelable<T>/writeParcelableArray<T>/readParcelableArray<T> | 读写Parcelable对象 |
| 接口描述符 | writeInterfaceToken(String)/readInterfaceToken() | 写入/读取接口描述符,本地对象可用其校验本次通信 |
| 异常标记 | writeNoException()/readException() | 写入"未发生异常"标记 / 读取异常 |
| 文件描述符 | writeFileDescriptor(Int32)/readFileDescriptor()/closeFileDescriptor(fd)/dupFileDescriptor(fd)/containFileDescriptors() | 跨进程传递、关闭、复制与检测 fd |
| 共享内存 | writeAshmem(Ashmem)/readAshmem() | 向 MessageSequence 写入/读取匿名共享对象 |
一个最简的完整写入-读取流程如下(组合自 cj-apis-rpc.md 示例):
// index.cj import kit.IPCKit.* import ohos.business_exception.BusinessException import kit.PerformanceAnalysisKit.Hilog try { let data = MessageSequence.create() let capacity = data.getRawDataCapacity() // 128MB data.writeInt(1024) data.writeString("hello ipc") let num = data.readInt() let str = data.readString() } catch (e: BusinessException) { Hilog.info(0, "test", "${e.message}") }从仓库 API 文档看,MessageSequence所有读写基础类型的方法在缓冲空间不足或读写顺序不一致时会抛出BusinessException(错误码 1900009/1900010),因此建议在写大量数据前先用getWritableBytes()确认剩余空间。
大数据传输:Ashmem 匿名共享内存
Ashmem 的设计初衷
在 IPC Kit 简介 的约束说明中指出:单个设备上跨进程通信时,传输的数据量最大为 200KB,过大的数据量需要使用匿名共享内存(Ashmem)。Ashmem只适用于本设备内跨进程通信,这一点在 API 文档中有明确说明。
内存保护类型常量
Ashmem类提供了四个静态保护类型常量(系统能力SystemCapability.Communication.IPC.Core,起始版本 22):
| 常量 | 值 | 含义 |
|---|---|---|
PROT_NONE | 0 | 映射的内存不可访问 |
PROT_READ | 1 | 映射的内存可读 |
PROT_WRITE | 2 | 映射的内存可写 |
PROT_EXEC | 4 | 映射的内存可执行 |
这些常量用于setProtectionType(protectionType: UInt32)与mapTypedAshmem(mapType: UInt32),可通过位或组合,例如Ashmem.PROT_READ | Ashmem.PROT_WRITE表示可读写映射。
Ashmem 的典型使用流程
Ashmem提供的方法包括:创建、关闭、映射和取消映射、读写数据、获取大小、设置保护等级。完整生命周期如下:
- 创建:
Ashmem.create(name: String, size: Int32),name用于查询信息(长度不能为 0),size以字节为单位(应大于 0);也可通过Ashmem.create(ashmem: Ashmem)复制现有对象的文件描述符(fd),使两个对象指向同一块共享内存区域; - 映射:写数据前需调用
mapReadWriteAshmem()建立可读写映射;只读场景可用mapReadonlyAshmem();如需指定保护等级则用mapTypedAshmem(mapType); - 读写:
writeDataToAshmem(buf: Array<Byte>, size: Int64, offset: Int64)写入数据,readDataFromAshmem(size: Int64, offset: Int64): Array<Byte>读取数据; - 设置保护:
setProtectionType(protectionType: UInt32)设置映射区域保护等级; - 解映射与关闭:关闭前必须先
unmapAshmem()解除地址映射,再closeAshmem()。
示例(创建 1MB 共享内存并写入读取,参见 cj-apis-rpc.md):
// index.cj import kit.IPCKit.* import ohos.business_exception.BusinessException import kit.PerformanceAnalysisKit.Hilog try { let ashmem = Ashmem.create("ashmem", 1024*1024) ashmem.mapReadWriteAshmem() ashmem.writeDataToAshmem([1], 1, 0) let buf = ashmem.readDataFromAshmem(1, 0) ashmem.unmapAshmem() ashmem.closeAshmem() } catch (e: BusinessException) { Hilog.info(0, "test", "${e.message}") }RPC 错误码速查与排查要点
RPC 相关的错误码集中在 RPC错误码 文档中,覆盖共享内存、序列化缓冲、IPC 对象与系统调用等场景,常用错误码归纳如下:
| 错误码 | 错误信息 | 主要可能原因 | 排查要点 |
|---|---|---|---|
| 1900001 | Failed to call mmap. | 映射区域过大;系统内存不足 | 检查Ashmem.create()指定的内存大小 |
| 1900002 | Failed to call ioctl. | 参数非法;类型超出映射时指定类型 | 参数须来自PROT_EXEC/PROT_READ/PROT_WRITE,且是映射类型的子集 |
| 1900003 | Failed to write data to the shared memory. | 写入内容超出映射大小;未设置PROT_WRITE | 检查写入总大小与写保护权限 |
| 1900004 | Failed to read data from the shared memory. | 读取内容超出映射大小;未设置PROT_READ | 检查读取范围与读保护权限 |
| 1900005 / 1900006 | Operation allowed only for the proxy/remote object. | 在RemoteObject上调用了仅RemoteProxy支持的方法(或反之) | 核对对象类型与所调方法的适用范围 |
| 1900007 | Communication failed. | 远端对象已销毁;代理已过期 | 注册死亡监听,检查远端对象生命周期 |
| 1900008 | The proxy or remote object is invalid. | 代理失效或远端对象已销毁 | 检查 Proxy 获取过程与远端析构状态 |
| 1900009 | Failed to write data to the message sequence. | sequence 默认空间已满 | 用getWritableBytes()等确认剩余空间 |
| 1900010 | Failed to read data from the message sequence. | 读取与写入顺序不一致 | 严格保证读写顺序一致 |
| 1900011 | Memory allocation failed. | 写入数据过大 | 检查写入数据量或参数是否过大 |
| 1900013 | Failed to call dup. | 进程 fd 资源耗尽;入参 fd 已关闭 | 检查 fd 有效性并排查 fd 泄漏 |
约束与限制总结
综合 IPC Kit 简介,使用 IPC/RPC 时必须遵守以下约束:
- 单次数据量上限:单个设备上跨进程通信时,传输的数据量最大为200KB;过大的数据量请使用匿名共享内存 Ashmem,同时注意
Ashmem仅适用于设备内跨进程通信。 - 匿名 Stub 死亡通知限制:不支持在 RPC 中订阅匿名 Stub 对象(即没有向 SAMgr 注册的 Stub 对象)的死亡通知。
- Proxy 二次传递限制:不支持把跨设备的 Proxy 对象传递回该 Proxy 对象所指向的 Stub 对象所在的设备,即指向远端设备 Stub 的 Proxy 对象不能在本设备内进行二次跨进程传递。
总结
IPC 与 RPC 是 OpenHarmony 仓颉应用实现跨进程、跨设备协作的基础设施:IPC 依托 Binder 驱动解决设备内进程隔离下的服务调用,RPC 依托软总线驱动把调用延伸到其他设备。开发者通过 Proxy/Stub 模型与 SAMgr 注册机制建立通信关系,再借助Parcelable、MessageSequence、Ashmem完成数据的封送、解封与大规模共享内存传输,同时受 200KB 单次传输上限、匿名 Stub 死亡通知与 Proxy 二次传递等约束。本文所引用的 API 细节与错误码均可在本仓库的 cj-apis-rpc.md 与 cj-errorcode-rpc.md 中继续深入查阅。
- 文档
- 知识库
- 教程
- OpenHarmony
【免费下载链接】CangjieCorpus
本仓库为仓颉编程语言语料库,聚焦 仓颉编程语言 核心知识体系,整合官方开发指南、API 文档、典型代码示例及语法规范等多维度资源,经结构化处理与专业标注,构建为支持 RAG(检索增强生成)技术 的高质量知识基座,适配智能编码助手、学习辅助工具等应用开发需求。 欢迎开发者基于本语料库构建仓颉语言智能应用,推动鸿蒙开发生态智能化升级!
相关推荐
仓颉语言 IPC Kit 进程间通信实战:ohos.rpc 模块 API 全解析与 RPC 错误码排障指南
仓颉语言 IPC Kit 进程间通信实战:ohos.rpc 模块 API 全解析与 RPC 错误码排障指南 本指南以 IPC Kit 官方 API 参考为骨架,
文档知识库教程OpenHarmony仓颉 RPC 通信错误码 1900001-1900013 全解析:OpenHarmony IPC Kit 进程间通信故障排查实战指南
仓颉 RPC 通信错误码 1900001 1900013 全解析:OpenHarmony IPC Kit 进程间通信故障排查实战指南 本篇技术指南以 OpenH
文档知识库教程OpenHarmonyYCBlogs IPC通信:Binder机制与跨进程通信详解
YCBlogs IPC通信:Binder机制与跨进程通信详解 Binder机制是Android系统中最重要的跨进程通信 IPC 方式,它为应用程序组件之间的通信
教程技术博客文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考