参考链接:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ipc-kit-V5
-
IPC与RPC通信概述
IPC(Inter-Process Communication)与RPC(Remote Procedure Call)用于实现跨进程通信。其中,前者使用Binder驱动,用于设备内的跨进程通信,后者使用软总线驱动,用于跨设备跨进程通信。
IPC和RPC通常采用客户端-服务器(Client-Server)模型,在使用时,请求服务的(Client)一端进程可获取提供服务(Server)一端所在进程的代理(Proxy),并通过此代理读写数据来实现进程间的数据通信,更具体的讲:
- 客户端调用:客户端调用本地代理对象的方法,本地代理对象封装了将请求发送到远程服务端的逻辑。
- 网络传输:请求通过网络传输到远程服务端。
- 服务端处理:服务端接收到请求,调用本地的服务实现(也称为存根Stub)执行请求的操作。
- 返回结果:服务端将结果返回给客户端,本地代理对象接收结果并传递给调用方。
下文直接使用Proxy表示服务请求方,Stub表示服务提供方。
-
IPC与RPC通信开发指导
能力(Ability)绑定和跨设备服务管理的相关操作:
-
-
添加相关依赖
-
// FA模型需要从@kit.AbilityKit导入featureAbility
// import { featureAbility } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';
-
-
绑定ability
-
能力(Ability)是指在HarmonyOS中,应用程序提供的功能单元或服务。
// 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);
}
- 构造变量 want:
- 在代码中首先定义了 want 变量,用于指定要绑定的能力(Ability)的详细信息。
- 对于单个设备的场景,want 包括 bundleName(应用包名)和 abilityName(能力名),这些值是实际的应用程序包名和能力组件名。
- 构造变量 connect:
- connect 变量定义了在绑定能力时的回调函数,包括 onConnect(连接成功时调用的函数)、onDisconnect(断开连接时调用的函数)和 onFailed(连接失败时调用的函数)。
- 这些回调函数用于处理绑定操作的结果和状态。
- 使用 context.connectServiceExtensionAbility 方法:
- context 对象是从 common.UIAbilityContext 中获取的,可能是用于UI能力上下文的操作接口。
- connectServiceExtensionAbility 方法用于建立能力(Ability)的连接,参数包括 want 和 connect 变量。
- connectId 是一个整数,用来标识建立连接后返回的唯一ID,可以在需要解绑服务时使用。
- 跨设备绑定的处理:
- 使用 distributedDeviceManager 的 createDeviceManager 方法创建了一个设备管理器实例 dmInstance,并指定了目标设备的标识符(通常是网络ID)。
- 如果 dmInstance 成功创建,可以通过它获取可用设备列表,并从中获取目标设备的 networkId。
- 构建了新的 want 变量,这次包括了 deviceId(设备ID)和 flags(标志位),用于跨设备绑定服务。
- 同样使用 context.connectServiceExtensionAbility 方法来建立跨设备的服务连接,同样需要保存返回的 connectId。
-
服务端处理客户端请求
-
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;
}
}
服务端的能力(Ability)可以通过 Stub 类实现对客户端请求的处理逻辑。
整体流程是客户端与服务端建立连接后,服务端返回一个处理请求的 Stub 对象,客户端可以通过这个对象发送消息并得到响应。
- 导入模块和类定义:
- import { rpc } from '@kit.IPCKit'; 导入了 IPCKit 模块中的 rpc 对象,用于处理进程间通信。
- import { Want } from '@kit.AbilityKit'; 导入了 AbilityKit 模块中的 Want 类,用于定义服务端要绑定的能力的详细信息。
- 定义 Stub 类:
- class Stub extends rpc.RemoteObject { ... }:这个类继承自 rpc.RemoteObject,是一个远程对象,用于处理客户端发送的消息和请求。
- 构造函数:
- constructor(descriptor: string) { super(descriptor); }:构造函数接受一个 descriptor 参数,并将其传递给父类 rpc.RemoteObject 的构造函数。descriptor 通常是能力的标识符或名称,用于唯一标识这个远程对象。
- 实现 onRemoteMessageRequest 方法:
- onRemoteMessageRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence, option: rpc.MessageOption): boolean | Promise<boolean>:这个方法是处理客户端请求的关键。当客户端发送消息给服务端时,服务端的 Stub 对象会收到 onRemoteMessageRequest 调用。参数包括:
- code:请求的类型或代码,用于区分不同的请求。
- data:消息序列,包含客户端发送的数据。
- reply:回复消息序列,用于向客户端发送响应。
- option:消息选项,可能包含额外的控制或配置信息。
- 在方法内部,根据 code 处理客户端的具体请求逻辑,并返回 true 表示请求处理成功。
- 实现 onConnect 方法:
- onConnect(want: Want):这个方法是在服务端被绑定的能力建立连接时调用的。
- 在方法内部,首先创建一个 Stub 类的实例 robj,并传入能力的描述符(例如 "rpcTestAbility")。
- 返回这个 robj 对象,这样当客户端与服务端的能力建立连接时,服务端会返回这个 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里读取结果
} finally {
result.data.reclaim();
result.reply.reclaim();
}
}
let options = new rpc.MessageOption();
let datas = rpc.MessageSequence.create();
let replys = rpc.MessageSequence.create();
// 往data里写入参数
if (proxy != undefined) {
proxy.sendMessageRequest(1, datas, replys, options, sendRequestCallback);
}
- 获取代理对象:
在客户端的 onConnect 回调函数中,客户端首先获取到服务端提供的代理对象 proxy。
- 发起请求:
使用代理对象的 sendMessageRequest 方法向服务端发送请求。请求中包含要发送的数据和参数,这些参数被打包到 data 对象中。
- 处理响应:
使用Promise:如果客户端选择使用Promise,即使用 .then() 方法处理异步操作的结果。在 then 方法中,客户端可以检查 result.errCode 来判断请求是否成功。如果 errCode 不为 0,表示请求失败,客户端可以记录错误信息到日志中。如果请求成功,客户端可以从 result.reply 中读取服务端返回的结果数据。最后,客户端需要调用 data.reclaim() 和 reply.reclaim() 来释放使用的资源。
使用回调函数:另一种处理方式是使用回调函数。客户端可以定义一个回调函数 sendRequestCallback,该函数接收两个参数:err 和 result。在回调函数中,客户端同样检查 result.errCode,处理错误情况并从 result.reply 中读取结果数据。类似地,无论请求成功或失败,客户端最后都需要调用 result.data.reclaim() 和 result.reply.reclaim() 来释放资源。
-
-
断开连接
-
IPC通信结束后,FA模型使用featureAbility的接口断开连接,Stage模型在获取context后用提供的接口断开连接。
// 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';
function disconnectCallback() {
hilog.info(0x0000, 'testTag', 'disconnect ability done');
}
// FA模型使用此方法断开连接
// featureAbility.disconnectAbility(connectId, disconnectCallback);
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, remote) => {
proxy = remote;
},
onDisconnect: (elementName) => {
},
onFailed: () => {
proxy;
}
};
// FA模型使用此方法连接服务
// connectId = featureAbility.connectAbility(want, connect);
connectId = this.context.connectServiceExtensionAbility(want,connect);
this.context.disconnectServiceExtensionAbility(connectId);
连接服务(Connect Service):
- FA模型使用了 connectServiceExtensionAbility 方法来连接到一个服务,具体是通过指定的 Want 对象来定义服务的包名和组件名。
- 当连接成功时,会触发 onConnect 回调函数,通过这个回调函数获取到远程对象 remote,并将其赋给 proxy 变量。
- 如果连接失败,则会调用 onFailed 回调函数。
- 断开连接(Disconnect):
- 当IPC通信完成或者不再需要时,FA模型使用 disconnectServiceExtensionAbility 方法来断开与服务的连接,参数是之前建立连接时获取的 connectId。
- 在断开连接时,可以提供一个回调函数 disconnectCallback,用于在断开连接完成时进行一些后续处理或者记录。
-
远端状态订阅开发实例
-
使用场景
-
IPC(Inter-Process Communication)和RPC(Remote Procedure Call)提供了对远端Stub对象状态的订阅机制,这种机制允许本地Proxy对象在远端Stub对象消亡时接收消亡通知并执行相应的清理操作。
- 订阅机制概述:
订阅和取消订阅:本地Proxy对象通过调用特定的接口来订阅远端Stub对象的消亡通知。通常这个接口会接受一个实现了DeathRecipient接口的对象,该接口需要实现onRemoteDied方法以处理远端Stub对象消亡时的清理逻辑。当不再需要订阅时,应调用另一个接口取消订阅。
消亡通知接口:在远端Stub对象所在进程消亡或者所在设备离开组网时,会触发onRemoteDied方法。这使得本地Proxy对象能够在远端Stub对象状态变化时感知并采取适当的措施。
- 使用场景:
Stub对象进程消亡通知:当远端Stub对象所在的进程意外退出时,本地Proxy对象能够收到消亡通知。这种情况下,Proxy可以清理相应的本地资源,防止资源泄漏或不一致状态。
Stub对象设备离网通知:如果Stub对象所在的设备离开了网络(比如断开连接或者关机),也会触发消亡通知。这使得本地Proxy可以根据实际情况来调整其行为或状态。
- 注意事项:
调用顺序和规范:在使用这些接口时,需要遵循特定的调用顺序和规范,以确保订阅和取消订阅的正确执行。
匿名Stub对象限制:当前RPC机制不支持匿名Stub对象的消亡通知。只有向SAMgr注册过的服务才能够被订阅消亡通知。而IPC则支持对匿名对象的消亡通知,这是两种机制的一点不同之处。
ArkTS侧接口
-
-
ArkTS侧接口
-
通过导入 @kit.AbilityKit、@kit.IPCKit 和 @kit.PerformanceAnalysisKit 中的模块,实现了与服务的连接和通信功能。它通过事件回调和服务描述信息,实现了连接建立、断开和失败处理的逻辑,同时确保在操作过程中能够正确地管理和使用 proxy 对象和 connectionId。
// 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';
let proxy: rpc.IRemoteObject | undefined;
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');
}
};
let want: Want = {
bundleName: "com.ohos.server",
abilityName: "com.ohos.server.EntryAbility",
};
// FA模型通过此方法连接服务
// FA.connectAbility(want, connect);
// 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
// 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
let connectionId = context.connectServiceExtensionAbility(want, connect);
- 定义变量:
- proxy: rpc.IRemoteObject | undefined;:定义了一个远程对象的变量 proxy,初值为 undefined。
- connect: common.ConnectOptions:定义了连接选项,包括 onConnect、onDisconnect 和 onFailed 三个回调函数。这些函数用于在连接建立、断开或失败时执行相应的操作。
- 服务请求信息:
want: Want:描述了要连接的服务的详细信息,包括服务所在的包名 (bundleName) 和能力名 (abilityName)。
- 建立连接:
通过获取当前上下文 (context),调用 connectServiceExtensionAbility 方法来连接指定的服务。连接建立成功后,返回一个 connectionId,这个 connectionId 在解绑服务时需要作为参数传入。
- 注册和注销死亡回调:
一旦 onConnect 回调被触发,proxy 对象就会被赋值为服务端返回的代理对象。此时,可以对 proxy 执行各种操作,比如注册死亡回调。
创建了一个 MyDeathRecipient 类来实现 rpc.DeathRecipient 接口。在 onRemoteDied 方法中,可以定义在服务端断开连接时需要执行的操作,代码中是记录一条日志。
使用 proxy.registerDeathRecipient(deathRecipient, 0) 方法注册死亡回调。这样,当服务端断开连接时,onRemoteDied 方法会被调用。
使用 proxy.unregisterDeathRecipient(deathRecipient, 0) 方法在不再需要监听服务端死亡事件时取消注册。
import { rpc } from '@kit.IPCKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
class MyDeathRecipient implements rpc.DeathRecipient{
onRemoteDied() {
hilog.info(0x0000, 'testTag', 'server died');
}
}
let deathRecipient = new MyDeathRecipient();
if (proxy != undefined) {
proxy.registerDeathRecipient(deathRecipient, 0);
proxy.unregisterDeathRecipient(deathRecipient, 0);
}
-
-
Stub感知Proxy消亡(匿名Stub的使用)
-
通过利用匿名Stub对象,可以实现在跨进程通信中使服务端的Stub感知客户端的Proxy消亡状态,从而实现反向的死消亡通知。这种技术对于处理资源管理和错误处理非常有用,但需要小心处理跨进程通信的限制和匿名Stub对象的生命周期管理。
// Proxy
int TestAbilityProxy::TestAnonymousStub()
{
MessageOption option;
MessageParcel dataParcel, replyParcel;
dataParcel.UpdateDataVersion(Remote());
dataParcel.WriteRemoteObject(new TestAbilityStub());
int error = Remote()->SendRequest(TRANS_ID_REVERSED_MONITOR,dataParcel, replyParcel, option);
int result = (error == ERR_NONE) ? replyParcel.ReadInt32() : -1;
return result;
}
// Stub
int TestAbilityStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)
{
switch (code) {
case TRANS_ID_REVERSED_MONITOR: {
sptr<IRemoteObject> obj = data.ReadRemoteObject();
if (obj == nullptr) {
reply.WriteInt32(ERR_NULL_OBJECT);
return ERR_NULL_OBJECT;
}
bool result = obj->AddDeathRecipient(new TestDeathRecipient());
result ? reply.WriteInt32(ERR_NONE) : reply.WriteInt32(-1);
break;
}
default:
break;
}
return ERR_NONE;
}
- 基本概念和流程:
正向的消亡通知:当Stub对象在服务端消亡时,Proxy对象在客户端可以感知到这一事件。
反向的死消亡通知:希望Stub在服务端可以感知到Proxy在客户端的消亡状态。
- 实现方法:
假设有两个进程 A 和 B:进程 A是Stub所在进程,负责处理客户端的请求。进程 B是Proxy所在进程,与进程 A 进行通信。
- 进程 B 获取进程 A 的 Proxy 对象:
- 创建匿名Stub对象:在进程 B 中,创建一个匿名的Stub对象,即一个未向 System Ability Manager (SAMgr) 注册的Stub对象。这个Stub对象是为了实现反向通知而存在的。
- 传递匿名Stub给进程 A 的原Stub:进程 B 将这个匿名Stub对象通过 SendRequest 等接口传递给进程 A 的原Stub对象。这可以通过网络传输,具体的实现依赖于通信的协议和框架。
- 进程 A 接收到进程 B 传递过来的匿名Stub对象,并将其持有。
- 匿名Stub的消亡感知:当进程 B 消亡或者设备离开组网时,匿名Stub对象在进程 B 中会被系统回收。
- 原Stub感知匿名Stub的消亡:
原Stub在进程 A 中设置了对匿名Stub对象的死亡监听器(Death Recipient)。当匿名Stub对象在进程 B 中消亡时,这个死亡监听器会被触发。原Stub收到这个通知后,就可以处理相关的逻辑,比如清理资源或者发出警告通知。
- 注意事项:
跨进程通信限制:这种技术仅限于设备内部的进程间通信,因为匿名Stub对象并未在系统整体的服务管理器(SAMgr)中注册,无法跨设备进行通信。
匿名Stub对象的自动回收:如果匿名Stub对象在进程 B 中没有被任何Proxy对象指向,系统会自动回收它。这种情况下,进程 A 的原Stub将不会收到消亡通知。
- 代码解释
利用匿名Stub实现Stub感知Proxy消亡的机制。
- Proxy 端代码 (TestAbilityProxy) TestAnonymousStub 方法:
创建了一个 MessageParcel 对象 dataParcel,并调用 UpdateDataVersion 方法来标记其为远程传输版本。
使用 dataParcel.WriteRemoteObject(new TestAbilityStub()) 将一个新的 TestAbilityStub 对象写入 dataParcel 中。这个对象即为匿名Stub,因为它没有向 System Ability Manager (SAMgr) 注册。
调用 Remote()->SendRequest 方法,将 dataParcel 发送给远程对象,通过指定的 TRANS_ID_REVERSED_MONITOR 标识符进行通信。
最终根据返回结果判断通信是否成功,并返回相应的结果。
- Stub 端代码 (TestAbilityStub) OnRemoteRequest 方法:
当收到 TRANS_ID_REVERSED_MONITOR 标识符的请求时,从 data 中读取 IRemoteObject 对象 obj。
检查 obj 是否为 nullptr,如果是,则返回错误代码 ERR_NULL_OBJECT。
否则,调用 obj->AddDeathRecipient(new TestDeathRecipient()),将一个 TestDeathRecipient 对象注册为 obj 的死亡监听器(Death Recipient)。
最后根据操作结果,向 reply 中写入相应的错误代码或成功代码。
-
IPC通信开发指导(C/C++)
-
场景介绍
-
- IPC的主要作用:
IPC的主要目的是让不同的进程(或者线程)能够在运行中相互通信和交换数据。这些进程可能运行在同一台计算机的不同内存空间中,或者分布在网络上的不同计算节点上。
在软件设计中,通常会有两种角色来实现IPC:Proxy(代理)和Stub(存根)。Proxy位于一个进程中,而Stub则位于另一个进程中。它们通过一定的IPC机制来进行通信,例如通过网络、共享内存、消息队列等方式。
- IPC CAPI接口:
IPC CAPI接口通常是指为了在C语言环境下实现IPC而提供的接口或库。这些接口提供了一种方式来处理进程间通信的底层细节,例如建立通道、发送和接收消息等。
与高级语言不同,IPC CAPI接口通常较为底层,需要开发人员显式地管理内存分配、消息格式等细节。
- 元能力(Meta-ability)的作用:
元能力可能包括了对通信协议的封装、安全性、错误处理、性能优化等方面的支持,使得开发者可以更专注于业务逻辑而不必关注底层的细节。
-
-
接口说明
-
表1 CAPI侧IPC接口:
-
-
开发步骤
- 添加动态链接库
-
# ipc capi
libipc_capi.so
# 元能力,ability capi
libchild_process.so
-
-
- 头文件
-
// ipc capi
#include <IPCKit/ipc_kit.h>
// 元能力,ability capi
#include <AbilityKit/native_child_process.h>
-
-
- 异步调用场景
-
- 公共数据及函数定义
#include <string>
#include <thread>
#include <mutex>
#include <chrono>
#include <condition_variable>
#include <IPCKit/ipc_kit.h>
#include <AbilityKit/native_child_process.h>
#include <hilog/log.h>
#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0x0201
#define LOG_TAG "IPCCApiSample"
enum RequestCode {
ASYNC_ADD_CODE = 1,
REQUEST_EXIT_CODE = 2,
OTHER_CODE
};
static constexpr int MAX_MEMORY_SIZE = 204800;
static const std::string INTERFACE_DESCRIPTOR = "INTERFACE_DESCRIPTOR";
static const std::string NATIVE_REMOTE_STUB_TEST_TOKEN = "native.remote.stub";
static const std::string NATIVE_REMOTE_STUB_ASYNC_CALL_TEST_TOKEN = "native.remote.stub.async.call";
// 定义内存分配函数
static void* LocalMemoryAllocator(int32_t len) {
if (len < 0 || len > MAX_MEMORY_SIZE ) {
return nullptr;
}
void *buffer = malloc(len);
if (buffer == nullptr) {
return nullptr;
}
memset(buffer, 0, len);
return buffer;
}
- 头文件:<string> 提供了 C++ 的字符串类支持,<thread> 和 <mutex> 提供了线程和互斥锁的支持,<chrono> 提供了时间支持,<condition_variable> 提供了条件变量的支持;<IPCKit/ipc_kit.h> 和 <AbilityKit/native_child_process.h> 可能是项目特定的库文件,用于处理 IPC 和子进程相关的功能;<hilog/log.h> 是一个日志记录的库文件。
- 宏定义:LOG_DOMAIN 和 LOG_TAG,用于日志记录。LOG_DOMAIN 设置为 0x0201,可能用于区分日志域。LOG_TAG 设置为 "IPCCApiSample",用于标识日志的标签。
- 枚举类型: RequestCode,包括三个枚举值:ASYNC_ADD_CODE 和 REQUEST_EXIT_CODE 分别设置为 1 和 2,OTHER_CODE 没有显式赋值,默认值为 3。
- 常量定义:MAX_MEMORY_SIZE 设置为 204800,定义了一个最大内存大小的常量;INTERFACE_DESCRIPTOR、NATIVE_REMOTE_STUB_TEST_TOKEN 和 NATIVE_REMOTE_STUB_ASYNC_CALL_TEST_TOKEN 都是字符串常量,用于标识接口描述符和测试用的远程存根 token。
- 函数定义:LocalMemoryAllocator 是一个静态函数,用于分配指定长度的内存。如果长度超出 MAX_MEMORY_SIZE 的范围,则返回 nullptr。否则,使用 malloc 分配内存,然后将其清零并返回分配的内存指针。
- 服务端对象: IpcCApiStubTest
定义一个名为 IpcCApiStubTest 的类,它主要用于处理异步的 IPC 操作和线程间的同步控制。
class IpcCApiStubTest {
public:
explicit IpcCApiStubTest();
~IpcCApiStubTest();
void MainProc();
OHIPCRemoteStub* GetRemoteStub();
static int OnRemoteRequest(uint32_t code, const OHIPCParcel *data, OHIPCParcel *reply, void *userData);
private:
int AsyncAdd(const OHIPCParcel *data);
int RequestExitChildProcess();
private:
OHIPCRemoteStub *stub_{ nullptr };
std::mutex childMutex_;
std::condition_variable childCondVar_;
};
IpcCApiStubTest::IpcCApiStubTest() {
stub_ = OH_IPCRemoteStub_Create(INTERFACE_DESCRIPTOR.c_str(), &IpcCApiStubTest::OnRemoteRequest,
nullptr, this);
}
IpcCApiStubTest::~IpcCApiStubTest() {
if (stub_ != nullptr) {
OH_IPCRemoteStub_Destroy(stub_);
}
}
void IpcCApiStubTest::MainProc() {
std::unique_lock<std::mutex> autoLock(childMutex_);
childCondVar_.wait(autoLock);
}
OHIPCRemoteStub* IpcCApiStubTest::GetRemoteStub() {
return stub_;
}
int IpcCApiStubTest::OnRemoteRequest(uint32_t code, const OHIPCParcel *data, OHIPCParcel *reply, void *userData) {
int readLen = 0;
char *token = nullptr;
// 接口校验
if (OH_IPCParcel_ReadInterfaceToken(data, &token, &readLen, LocalMemoryAllocator) != OH_IPC_SUCCESS
|| NATIVE_REMOTE_STUB_TEST_TOKEN != token) {
if (token != nullptr) {
OH_LOG_ERROR(LOG_APP, "check InterfaceToken failed");
free(token);
}
return OH_IPC_PARCEL_WRITE_ERROR;
}
free(token);
auto *stubTest = reinterpret_cast<IpcCApiStubTest *>(userData);
if (stubTest == nullptr) {
return OH_IPC_CHECK_PARAM_ERROR;
}
auto rqCode = RequestCode(code);
switch (rqCode) {
case ASYNC_ADD_CODE: {
return stubTest->AsyncAdd(data);
}
case REQUEST_EXIT_CODE: {
return stubTest->RequestExitChildProcess();
}
default:
break;
}
return OH_IPC_SUCCESS;
}
int IpcCApiStubTest::AsyncAdd(const OHIPCParcel *data) {
int a = 0;
int b = 0;
OH_LOG_INFO(LOG_APP, "start async add a=%d,b=%d", a, b);
if ((OH_IPCParcel_ReadInt32(data, &a) != OH_IPC_SUCCESS)
|| (OH_IPCParcel_ReadInt32(data, &b) != OH_IPC_SUCCESS)) {
return OH_IPC_PARCEL_READ_ERROR;
}
auto proxyCallBack = OH_IPCParcel_ReadRemoteProxy(data);
if (proxyCallBack == nullptr) {
return OH_IPC_PARCEL_READ_ERROR;
}
OH_LOG_INFO(LOG_APP, "start create sendCallBack thread!");
// 此处开启线程异步完成功能实现并利用proxyCallBack完成结果响应,如果同步调用,则直接通过replyData写入响应结果即可
std::thread th([proxyCallBack, a, b] {
auto data = OH_IPCParcel_Create();
if (data == nullptr) {
OH_IPCRemoteProxy_Destroy(proxyCallBack);
return;
}
auto reply = OH_IPCParcel_Create();
if (reply == nullptr) {
OH_IPCParcel_Destroy(data);
OH_IPCRemoteProxy_Destroy(proxyCallBack);
return;
}
if (OH_IPCParcel_WriteInt32(data, a + b) != OH_IPC_SUCCESS) {
OH_IPCParcel_Destroy(data);
OH_IPCParcel_Destroy(reply);
OH_IPCRemoteProxy_Destroy(proxyCallBack);
return;
}
// 异步线程处理结果通过IPC同步调用方式返回给业务请求方
OH_IPC_MessageOption option = { OH_IPC_REQUEST_MODE_SYNC, 0 };
OH_LOG_INFO(LOG_APP, "thread start sendCallBack!");
int ret = OH_IPCRemoteProxy_SendRequest(proxyCallBack, ASYNC_ADD_CODE, data, reply, &option);
OH_LOG_INFO(LOG_APP, "thread sendCallBack ret = %d", ret);
if (ret != OH_IPC_SUCCESS) {
OH_IPCParcel_Destroy(data);
OH_IPCParcel_Destroy(reply);
OH_IPCRemoteProxy_Destroy(proxyCallBack);
return;
}
OH_IPCRemoteProxy_Destroy(proxyCallBack);
OH_IPCParcel_Destroy(data);
OH_IPCParcel_Destroy(reply);
});
th.detach();
return OH_IPC_SUCCESS;
}
int IpcCApiStubTest::RequestExitChildProcess() {
std::unique_lock<std::mutex> autoLock(childMutex_);
childCondVar_.notify_all();
return OH_IPC_SUCCESS;
}
- 类定义:
IpcCApiStubTest 类声明了几个公共方法和静态方法,以及一些私有成员变量。公共方法包括构造函数、析构函数、MainProc 方法、GetRemoteStub 方法和静态的 OnRemoteRequest 方法。私有方法包括 AsyncAdd 方法和 RequestExitChildProcess 方法。类中还包含了一个指向 OHIPCRemoteStub 对象的指针 stub_,以及用于线程同步的 std::mutex 和 std::condition_variable。
- 构造函数和析构函数:
构造函数 IpcCApiStubTest() 是显式声明的构造函数,初始化了 stub_ 对象,调用 OH_IPCRemoteStub_Create 函数创建 IPC 远程存根对象,并注册了类的静态成员函数 OnRemoteRequest 作为 IPC 请求处理的回调函数。
析构函数 ~IpcCApiStubTest() 释放了 stub_ 对象,通过调用 OH_IPCRemoteStub_Destroy 函数。
- MainProc 方法:
MainProc 方法是一个公共方法,使用 std::unique_lock<std::mutex> 对 childMutex_ 上锁,并调用 childCondVar_.wait(autoLock) 使当前线程等待。这通常用于线程间的条件变量同步,直到收到通知(notify)才继续执行。
- GetRemoteStub 方法:
GetRemoteStub 方法返回 stub_ 成员变量,即当前的 IPC 远程存根对象的指针。
- OnRemoteRequest 静态方法:
OnRemoteRequest 是一个静态方法,用于处理远程 IPC 请求。它首先进行接口校验,验证传入的 data 中的接口令牌,并根据请求的 code 调用相应的成员方法 AsyncAdd 或 RequestExitChildProcess。
- AsyncAdd 方法:
AsyncAdd 方法是一个私有方法,处理异步加法操作。它从 data 中读取两个整数,然后创建一个新的线程执行加法操作,并通过 IPC 通信返回结果。
- RequestExitChildProcess 方法:
RequestExitChildProcess 方法是一个私有方法,用于通知子进程退出。它通过 childCondVar_.notify_one() 通知正在等待的线程,即 MainProc 方法中的等待将被唤醒,以便退出子进程。
- AsyncAdd 方法:
这个方法用于执行异步加法操作。首先,它从传入的 data 参数中读取两个整数 a 和 b。然后,它获取一个远程代理回调对象 proxyCallBack,如果无法获取或者读取数据失败,方法会返回错误码。
接下来,它创建一个新的线程,线程内部进行加法计算并将结果写入 OH_IPCParcel 对象中。然后,使用 OH_IPCRemoteProxy_SendRequest 方法将数据发送给远程代理,并等待处理结果。
最后,线程结束并分离,方法返回异步操作的成功状态。
- RequestExitChildProcess 方法:
这个方法用于通知子进程退出。它使用互斥锁 childMutex_ 和条件变量 childCondVar_ 来确保线程安全。
方法内部调用 notify_all() 方法唤醒所有等待在 childCondVar_ 上的线程,通知它们子进程可以退出。
最后,方法返回表示成功的状态码。
- 客户端代理对象: IpcCApiProxyTest
这些函数共同组成了一个处理与远程IPC代理通信的类 IpcCApiProxyTest。它使用了 OHIPCParcel 和 OHIPCRemoteProxy 等IPC相关的函数和结构体来实现同步和异步通信,并通过互斥锁和条件变量确保线程安全。每个函数都承担着特定的角色,从发送请求、处理回复到资源清理,保证了整个通信过程的有效性和稳定性。
// 用戶自定义错误码
static constexpr int OH_IPC_CREATE_OBJECT_ERROR = OH_IPC_USER_ERROR_CODE_MIN + 1;
class IpcCApiProxyTest {
public:
explicit IpcCApiProxyTest(OHIPCRemoteProxy *proxy);
~IpcCApiProxyTest();
public:
int AsyncAdd(int a, int b, int &result);
int RequestExitChildProcess();
void ClearResource();
private:
void SendAsyncReply(int &replyValue);
int WaitForAsyncReply(int timeOut);
static int OnRemoteRequest(uint32_t code, const OHIPCParcel *data,
OHIPCParcel *reply, void *userData);
static void OnDeathRecipientCB(void *userData);
private:
int asyncReply_{};
std::mutex mutex_;
std::condition_variable cv_;
OHIPCRemoteProxy *proxy_{ nullptr };
OHIPCRemoteStub *replyStub_{ nullptr };
OHIPCDeathRecipient *deathRecipient_{ nullptr };
};
IpcCApiProxyTest::IpcCApiProxyTest(OHIPCRemoteProxy *proxy) {
if (proxy == nullptr) {
OH_LOG_ERROR(LOG_APP, "proxy is nullptr");
return;
}
proxy_ = proxy;
replyStub_ = OH_IPCRemoteStub_Create(NATIVE_REMOTE_STUB_ASYNC_CALL_TEST_TOKEN.c_str(), OnRemoteRequest,
nullptr, this);
if (replyStub_ == nullptr) {
OH_LOG_ERROR(LOG_APP, "crete reply stub failed!");
return;
}
deathRecipient_ = OH_IPCDeathRecipient_Create(OnDeathRecipientCB, nullptr, this);
if (deathRecipient_ == nullptr) {
OH_LOG_ERROR(LOG_APP, "OH_IPCDeathRecipient_Create failed!");
return;
}
OH_IPCRemoteProxy_AddDeathRecipient(proxy_, deathRecipient_);
}
IpcCApiProxyTest::~IpcCApiProxyTest() {
if (proxy_ != nullptr) {
OH_IPCRemoteProxy_Destroy(proxy_);
}
if (deathRecipient_ != nullptr) {
OH_IPCDeathRecipient_Destroy(deathRecipient_);
}
if (replyStub_ != nullptr) {
OH_IPCRemoteStub_Destroy(replyStub_);
}
}
int IpcCApiProxyTest::AsyncAdd(int a, int b, int &result) {
OH_LOG_INFO(LOG_APP, "start %d + %d", a, b);
auto data = OH_IPCParcel_Create();
if (data == nullptr) {
return OH_IPC_CREATE_OBJECT_ERROR;
}
// 写入接口校验token
if (OH_IPCParcel_WriteInterfaceToken(data, NATIVE_REMOTE_STUB_TEST_TOKEN.c_str()) != OH_IPC_SUCCESS) {
OH_LOG_ERROR(LOG_APP, "OH_IPCParcel_WriteInterfaceToken failed!");
OH_IPCParcel_Destroy(data);
return OH_IPC_PARCEL_WRITE_ERROR;
}
if (OH_IPCParcel_WriteInt32(data, a) != OH_IPC_SUCCESS
|| OH_IPCParcel_WriteInt32(data, b) != OH_IPC_SUCCESS
|| OH_IPCParcel_WriteRemoteStub(data, replyStub_) != OH_IPC_SUCCESS) {
OH_IPCParcel_Destroy(data);
return OH_IPC_PARCEL_WRITE_ERROR;
}
// 异步发送使用replyStub_进行响应结果接收,异步处理需要写入用于接收结果的OHIPCRemoteStub对象
OH_IPC_MessageOption option = { OH_IPC_REQUEST_MODE_ASYNC, 0 };
int ret = OH_IPCRemoteProxy_SendRequest(proxy_, RequestCode::ASYNC_ADD_CODE, data, nullptr, &option);
if (ret != OH_IPC_SUCCESS) {
OH_IPCParcel_Destroy(data);
OH_LOG_ERROR(LOG_APP, "OH_IPCRemoteProxy_SendRequest failed!");
return ret;
}
static constexpr int TIMEOUT = 3;
WaitForAsyncReply(TIMEOUT);
OH_LOG_INFO(LOG_APP, "asyncReply_:%d", asyncReply_);
result = asyncReply_;
OH_IPCParcel_Destroy(data);
OH_IPCParcel_Destroy(reply);
return OH_IPC_SUCCESS;
}
int IpcCApiProxyTest::RequestExitChildProcess() {
auto data = OH_IPCParcel_Create();
if (data == nullptr) {
return OH_IPC_CREATE_OBJECT_ERROR;
}
auto reply = OH_IPCParcel_Create();
if (reply == nullptr) {
OH_IPCParcel_Destroy(data);
return OH_IPC_CREATE_OBJECT_ERROR;
}
if (OH_IPCParcel_WriteInterfaceToken(data, NATIVE_REMOTE_STUB_TEST_TOKEN.c_str()) != OH_IPC_SUCCESS) {
OH_LOG_ERROR(LOG_APP, "OH_IPCParcel_WriteInterfaceToken failed!");
OH_IPCParcel_Destroy(data);
OH_IPCParcel_Destroy(reply);
return OH_IPC_PARCEL_WRITE_ERROR;
}
OH_IPC_MessageOption option = { OH_IPC_REQUEST_MODE_SYNC, 0 };
int ret = OH_IPCRemoteProxy_SendRequest(proxy_, RequestCode::REQUEST_EXIT_CODE, data, reply, &option);
if (ret != OH_IPC_SUCCESS) {
OH_IPCParcel_Destroy(data);
OH_IPCParcel_Destroy(reply);
OH_LOG_ERROR(LOG_APP, "OH_IPCRemoteProxy_SendRequest failed!");
return ret;
}
OH_IPCParcel_Destroy(data);
OH_IPCParcel_Destroy(reply);
return OH_IPC_SUCCESS;
}
void IpcCApiProxyTest::SendAsyncReply(int &replyValue) {
std::unique_lock<std::mutex> lck(mutex_);
asyncReply_ = replyValue;
cv_.notify_all();
}
int IpcCApiProxyTest::WaitForAsyncReply(int timeOut) {
asyncReply_ = 0;
std::unique_lock<std::mutex> lck(mutex_);
cv_.wait_for(lck, std::chrono::seconds(timeOut), [&] {
return asyncReply_ != 0;
});
return asyncReply_;
}
int IpcCApiProxyTest::OnRemoteRequest(uint32_t code, const OHIPCParcel *data,
OHIPCParcel *reply, void *userData) {
OH_LOG_INFO(LOG_APP, "start %u", code);
auto *proxyTest = reinterpret_cast<IpcCApiProxyTest *>(userData);
if (proxyTest == nullptr || code != static_cast<uint32_t>(RequestCode::ASYNC_ADD_CODE)) {
OH_LOG_ERROR(LOG_APP, "check param failed!");
return OH_IPC_CHECK_PARAM_ERROR;
}
int32_t val = -1;
if (OH_IPCParcel_ReadInt32(data, &val) != OH_IPC_SUCCESS) {
OH_LOG_ERROR(LOG_APP, "OH_IPCParcel_ReadInt32 failed!");
return OH_IPC_PARCEL_READ_ERROR;
}
proxyTest->SendAsyncReply(val);
return OH_IPC_SUCCESS;
}
void IpcCApiProxyTest::ClearResource() {
// clear resource;
}
void IpcCApiProxyTest::OnDeathRecipientCB(void *userData) {
auto *proxyTest = reinterpret_cast<IpcCApiProxyTest *>(userData);
if (proxyTest != nullptr) {
proxyTest->ClearResource();
}
OH_LOG_INFO(LOG_APP, "the stub is dead!");
}
下面是对代码的解释:
- 构造函数 IpcCApiProxyTest::IpcCApiProxyTest:
构造函数接受一个 OHIPCRemoteProxy 类型的指针作为参数,用于初始化类的成员变量。如果传入的 proxy 参数为空指针,则记录错误日志并返回。创建一个 replyStub_ 对象,用于处理远程请求的响应。创建一个 deathRecipient_ 对象,用于处理远程代理对象销毁时的回调。将 deathRecipient_ 注册到 proxy_ 上,以便在代理销毁时执行特定操作。
- 析构函数 IpcCApiProxyTest::~IpcCApiProxyTest:
析构函数负责释放类中使用的资源,包括 proxy_、deathRecipient_ 和 replyStub_。.检查每个资源是否为空,如果不为空则调用相应的销毁函数进行资源释放。
- IpcCApiProxyTest::AsyncAdd:
用于向远程代理发送异步加法请求。
首先创建一个 OHIPCParcel 对象 data,用于封装要发送的数据。写入接口校验token和需要传递的整型参数 a 和 b 到 data 中。使用 replyStub_ 写入远程调用所需的信息。设置 OH_IPC_MessageOption 结构体,指定请求为异步模式,并调用 OH_IPCRemoteProxy_SendRequest 函数发送请求。如果发送请求失败,则记录错误并返回相应的错误码。使用 WaitForAsyncReply 函数等待异步响应,然后将响应结果写入 result 变量中。最后销毁 data 对象,并返回成功状态码 OH_IPC_SUCCESS。
- int IpcCApiProxyTest::RequestExitChildProcess()
用于向远程IPC代理发送一个同步请求来退出子进程。
首先,创建两个 OHIPCParcel 对象 data 和 reply,用于封装请求数据和接收回复数据。如果创建 data 或 reply 失败,则返回错误码 OH_IPC_CREATE_OBJECT_ERROR。使用 OH_IPCParcel_WriteInterfaceToken 将接口校验token写入 data,用于验证请求的合法性。设置 OH_IPC_MessageOption 结构体,指定请求为同步模式,并调用 OH_IPCRemoteProxy_SendRequest 发送请求。如果发送请求失败,则记录错误并返回相应的错误码。最后,释放 data 和 reply 对象,返回成功状态码 OH_IPC_SUCCESS。
- void IpcCApiProxyTest::SendAsyncReply(int &replyValue)
用于向异步请求的调用者发送回复。
使用 std::unique_lock<std::mutex> 锁住 mutex_,确保线程安全。将传入的 replyValue 赋值给 asyncReply_,表示异步操作的结果。调用 cv_.notify_all() 通知所有等待的线程,异步回复已经准备好。
- int IpcCApiProxyTest::WaitForAsyncReply(int timeOut)
用于等待异步回复的到来。
首先将 asyncReply_ 置为0,表示当前没有接收到任何回复。使用 std::unique_lock<std::mutex> 锁住 mutex_,确保线程安全。调用 cv_.wait_for() 等待 timeOut 秒,直到收到 cv_.notify_all() 通知或超时。Lambda 表达式 [] { return asyncReply_ != 0; } 用于判断是否收到回复。返回收到的 asyncReply_ 值。
- int IpcCApiProxyTest::OnRemoteRequest(uint32_t code, const OHIPCParcel *data, OHIPCParcel *reply, void *userData)
这个静态成员函数是远程IPC请求到达时的回调处理函数。
打印日志,记录请求的 code。将 userData 转换为 IpcCApiProxyTest* 类型的指针 proxyTest,用于后续操作。如果 proxyTest 为空指针或者收到的请求 code 不是预期的 ASYNC_ADD_CODE,记录错误并返回 OH_IPC_CHECK_PARAM_ERROR。使用 OH_IPCParcel_ReadInt32 从 data 中读取整型数据。调用 proxyTest->SendAsyncReply(val) 向调用者发送异步回复。返回 OH_IPC_SUCCESS 表示处理成功。
- void IpcCApiProxyTest::ClearResource()
用于清理资源,是在远程代理对象销毁时调用的回调函数。
- void IpcCApiProxyTest::OnDeathRecipientCB(void *userData)
这个静态成员函数是远程代理对象销毁时的回调函数。将 userData 转换为 IpcCApiProxyTest* 类型的指针 proxyTest。如果 proxyTest 不为空,则调用 proxyTest->ClearResource() 清理资源。记录日志表明远程代理对象已经销毁。
- 服务端调用入口,服务端文件"libipcCapiDemo.so"
通过定义全局对象和两个C语言外部接口函数,实现了一个简单的IPC服务端接口。
IpcCApiStubTest g_ipcStubObj;
#ifdef __cplusplus
extern "C" {
// 服务需要实现如下函数,具体可参考元能力接口说明
OHIPCRemoteStub* NativeChildProcess_OnConnect() {
OH_LOG_INFO(LOG_APP, "NativeChildProcess_OnConnect");
return g_ipcStubObj.GetRemoteStub();
}
void NativeChildProcess_MainProc() {
OH_LOG_INFO(LOG_APP, "NativeChildProcess_MainProc");
g_ipcStubObj.MainProc();
OH_LOG_INFO(LOG_APP, "NativeChildProcess_MainProc End");
}
}
#endif
- 全局对象声明:IpcCApiStubTest g_ipcStubObj;
定义了一个全局对象 g_ipcStubObj,这个对象的类型为 IpcCApiStubTest。它被用来管理和处理IPC通信的相关功能。
- extern "C" 块:
使用 extern "C" 声明,这段代码表明以下内容是使用C语言的链接规则编写的,以便能够被其他编程语言或环境调用。
- NativeChildProcess_OnConnect 函数:
NativeChildProcess_OnConnect 函数在子进程连接时被调用。它记录一个信息日志,然后通过 g_ipcStubObj 对象调用 GetRemoteStub() 方法,获取一个远程通信的Stub对象,并将其返回。
- NativeChildProcess_MainProc 函数:
NativeChildProcess_MainProc 函数定义了子进程的主处理过程。它首先记录一个信息日志,然后通过 g_ipcStubObj 对象调用 MainProc() 方法执行主处理逻辑,最后再次记录一个信息日志表示主处理过程结束。
- 客户端调用入口
该利用IPC实现了与动态链接库中子进程的通信,通过创建和管理 IpcCApiProxyTest 对象 g_ipcProxy,实现了向子进程发送请求并获取结果的功能。
IpcCApiProxyTest *g_ipcProxy = nullptr;
// 元能力打通IPC通道回调接口
void OnNativeChildProcessStarted(int errCode, OHIPCRemoteProxy *remoteProxy) {
OH_LOG_INFO(LOG_APP, "OnNativeChildProcessStarted proxy=%{public}p err=%{public}d", remoteProxy, errCode);
if (remoteProxy == nullptr) {
return;
}
g_ipcProxy = new (std::nothrow) IpcCApiProxyTest(remoteProxy);
if (g_ipcProxy == nullptr) {
OH_IPCRemoteProxy_Destroy(remoteProxy);
OH_LOG_ERROR(LOG_APP, "Alloc IpcCApiProxyTest object failed");
return;
}
}
int main(int argc, char *argv[]) {
int32_t ret = OH_Ability_CreateNativeChildProcess("libipcCapiDemo.so", OnNativeChildProcessStarted);
if (ret != 0) {
return -1;
}
if (g_ipcProxy == nullptr) {
return -1;
}
int a = 2;
int b = 3;
int result = 0;
ret = g_ipcProxy->AsyncAdd(a, b, result);
OH_LOG_INFO(LOG_APP, "AsyncAdd: %d + %d = %d, ret=%d", a, b, result, ret);
//kill stub端
ret = g_ipcProxy->RequestExitChildProcess();
//控制台输出: the stub is dead!
if (g_ipcProxy != nullptr) {
delete g_ipcProxy;
g_ipcProxy = nullptr;
}
return 0;
}
- OnNativeChildProcessStarted 函数:
当子进程启动后,该函数被调用。它接收一个错误码 errCode 和一个 OHIPCRemoteProxy 对象 remoteProxy 作为参数。函数首先记录启动信息的日志,然后检查 remoteProxy 是否为 nullptr。如果是 nullptr,说明创建IPC代理失败或子进程启动失败,函数直接返回。否则,它尝试创建一个 IpcCApiProxyTest 对象 g_ipcProxy,该对象用于管理和调用 remoteProxy 提供的功能。
- main 函数:
main 函数首先调用 OH_Ability_CreateNativeChildProcess 函数来创建一个本地子进程,传入动态链接库的名称和 OnNativeChildProcessStarted 作为回调函数。
如果创建子进程的返回值 ret 不为 0,说明创建失败,程序返回 -1。如果 g_ipcProxy 仍然为 nullptr,也即子进程启动后未成功创建 g_ipcProxy 对象,程序同样返回 -1。
通过 g_ipcProxy 调用子进程中的函数 AsyncAdd 进行异步加法操作,并记录操作的结果和返回值。通过调用 RequestExitChildProcess 方法向子进程发送终止请求。程序删除 g_ipcProxy 对象并置其为 nullptr,确保资源正确释放。