Harmony OS NEXT Developer Beta2的IPC Kit

参考链接:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ipc-kit-V5

  1. IPC与RPC通信概述

IPC(Inter-Process Communication)与RPC(Remote Procedure Call)用于实现跨进程通信。其中,前者使用Binder驱动,用于设备内的跨进程通信,后者使用软总线驱动,用于跨设备跨进程通信。

IPC和RPC通常采用客户端-服务器(Client-Server)模型,在使用时,请求服务的(Client)一端进程可获取提供服务(Server)一端所在进程的代理(Proxy),并通过此代理读写数据来实现进程间的数据通信,更具体的讲:

  1. 客户端调用:客户端调用本地代理对象的方法,本地代理对象封装了将请求发送到远程服务端的逻辑。
  2. 网络传输:请求通过网络传输到远程服务端。
  3. 服务端处理:服务端接收到请求,调用本地的服务实现(也称为存根Stub)执行请求的操作。
  4. 返回结果:服务端将结果返回给客户端,本地代理对象接收结果并传递给调用方。

下文直接使用Proxy表示服务请求方,Stub表示服务提供方。

  1. IPC与RPC通信开发指导

能力(Ability)绑定和跨设备服务管理的相关操作:

    1.  添加相关依赖

 

// FA模型需要从@kit.AbilityKit导入featureAbility
// import { featureAbility } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';

 

    1.  绑定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);
}

 

  1. 构造变量 want:
  • 在代码中首先定义了 want 变量,用于指定要绑定的能力(Ability)的详细信息。
  • 对于单个设备的场景,want 包括 bundleName(应用包名)和 abilityName(能力名),这些值是实际的应用程序包名和能力组件名。
  1. 构造变量 connect:
  • connect 变量定义了在绑定能力时的回调函数,包括 onConnect(连接成功时调用的函数)、onDisconnect(断开连接时调用的函数)和 onFailed(连接失败时调用的函数)。
  • 这些回调函数用于处理绑定操作的结果和状态。
  1. 使用 context.connectServiceExtensionAbility 方法:
  1. context 对象是从 common.UIAbilityContext 中获取的,可能是用于UI能力上下文的操作接口。
  2. connectServiceExtensionAbility 方法用于建立能力(Ability)的连接,参数包括 want 和 connect 变量。
  3. connectId 是一个整数,用来标识建立连接后返回的唯一ID,可以在需要解绑服务时使用。
  1. 跨设备绑定的处理:
  1. 使用 distributedDeviceManager 的 createDeviceManager 方法创建了一个设备管理器实例 dmInstance,并指定了目标设备的标识符(通常是网络ID)。
  2. 如果 dmInstance 成功创建,可以通过它获取可用设备列表,并从中获取目标设备的 networkId。
  3. 构建了新的 want 变量,这次包括了 deviceId(设备ID)和 flags(标志位),用于跨设备绑定服务。
  4. 同样使用 context.connectServiceExtensionAbility 方法来建立跨设备的服务连接,同样需要保存返回的 connectId。
    1. 服务端处理客户端请求

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 对象,客户端可以通过这个对象发送消息并得到响应。

  1. 导入模块和类定义:
  1. import { rpc } from '@kit.IPCKit'; 导入了 IPCKit 模块中的 rpc 对象,用于处理进程间通信。
  2. import { Want } from '@kit.AbilityKit'; 导入了 AbilityKit 模块中的 Want 类,用于定义服务端要绑定的能力的详细信息。
  1. 定义 Stub 类:
  1. class Stub extends rpc.RemoteObject { ... }:这个类继承自 rpc.RemoteObject,是一个远程对象,用于处理客户端发送的消息和请求。
  1. 构造函数:
  1. constructor(descriptor: string) { super(descriptor); }:构造函数接受一个 descriptor 参数,并将其传递给父类 rpc.RemoteObject 的构造函数。descriptor 通常是能力的标识符或名称,用于唯一标识这个远程对象。
  1. 实现 onRemoteMessageRequest 方法:
  1. onRemoteMessageRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence, option: rpc.MessageOption): boolean | Promise&lt;boolean&gt;:这个方法是处理客户端请求的关键。当客户端发送消息给服务端时,服务端的 Stub 对象会收到 onRemoteMessageRequest 调用。参数包括:
  2. code:请求的类型或代码,用于区分不同的请求。
  3. data:消息序列,包含客户端发送的数据。
  4. reply:回复消息序列,用于向客户端发送响应。
  5. option:消息选项,可能包含额外的控制或配置信息。
  6. 在方法内部,根据 code 处理客户端的具体请求逻辑,并返回 true 表示请求处理成功。
  1. 实现 onConnect 方法:
  1. onConnect(want: Want):这个方法是在服务端被绑定的能力建立连接时调用的。
  2. 在方法内部,首先创建一个 Stub 类的实例 robj,并传入能力的描述符(例如 "rpcTestAbility")。
  3. 返回这个 robj 对象,这样当客户端与服务端的能力建立连接时,服务端会返回这个 robj 对象,使得客户端可以通过这个对象发送消息和请求。
    1. 客户端处理服务端响应

客户端在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);
}
  1. 获取代理对象:

在客户端的 onConnect 回调函数中,客户端首先获取到服务端提供的代理对象 proxy。

  1. 发起请求:

使用代理对象的 sendMessageRequest 方法向服务端发送请求。请求中包含要发送的数据和参数,这些参数被打包到 data 对象中。

  1. 处理响应:

使用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() 来释放资源。

    1.  断开连接

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):

  1. FA模型使用了 connectServiceExtensionAbility 方法来连接到一个服务,具体是通过指定的 Want 对象来定义服务的包名和组件名。
  2. 当连接成功时,会触发 onConnect 回调函数,通过这个回调函数获取到远程对象 remote,并将其赋给 proxy 变量。
  3. 如果连接失败,则会调用 onFailed 回调函数。
  4. 断开连接(Disconnect):
  1. 当IPC通信完成或者不再需要时,FA模型使用 disconnectServiceExtensionAbility 方法来断开与服务的连接,参数是之前建立连接时获取的 connectId。
  2. 在断开连接时,可以提供一个回调函数 disconnectCallback,用于在断开连接完成时进行一些后续处理或者记录。
  1. 远端状态订阅开发实例

    1.  使用场景

IPC(Inter-Process Communication)和RPC(Remote Procedure Call)提供了对远端Stub对象状态的订阅机制,这种机制允许本地Proxy对象在远端Stub对象消亡时接收消亡通知并执行相应的清理操作。

  1. 订阅机制概述:

订阅和取消订阅:本地Proxy对象通过调用特定的接口来订阅远端Stub对象的消亡通知。通常这个接口会接受一个实现了DeathRecipient接口的对象,该接口需要实现onRemoteDied方法以处理远端Stub对象消亡时的清理逻辑。当不再需要订阅时,应调用另一个接口取消订阅。

消亡通知接口:在远端Stub对象所在进程消亡或者所在设备离开组网时,会触发onRemoteDied方法。这使得本地Proxy对象能够在远端Stub对象状态变化时感知并采取适当的措施。

  1. 使用场景:

Stub对象进程消亡通知:当远端Stub对象所在的进程意外退出时,本地Proxy对象能够收到消亡通知。这种情况下,Proxy可以清理相应的本地资源,防止资源泄漏或不一致状态。

Stub对象设备离网通知:如果Stub对象所在的设备离开了网络(比如断开连接或者关机),也会触发消亡通知。这使得本地Proxy可以根据实际情况来调整其行为或状态。

  1. 注意事项:

调用顺序和规范:在使用这些接口时,需要遵循特定的调用顺序和规范,以确保订阅和取消订阅的正确执行。

匿名Stub对象限制:当前RPC机制不支持匿名Stub对象的消亡通知。只有向SAMgr注册过的服务才能够被订阅消亡通知。而IPC则支持对匿名对象的消亡通知,这是两种机制的一点不同之处。

ArkTS侧接口

    1.  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);

 

  1. 定义变量:
  1. proxy: rpc.IRemoteObject | undefined;:定义了一个远程对象的变量 proxy,初值为 undefined。
  2. connect: common.ConnectOptions:定义了连接选项,包括 onConnect、onDisconnect 和 onFailed 三个回调函数。这些函数用于在连接建立、断开或失败时执行相应的操作。
  1. 服务请求信息:

want: Want:描述了要连接的服务的详细信息,包括服务所在的包名 (bundleName) 和能力名 (abilityName)。

  1. 建立连接:

通过获取当前上下文 (context),调用 connectServiceExtensionAbility 方法来连接指定的服务。连接建立成功后,返回一个 connectionId,这个 connectionId 在解绑服务时需要作为参数传入。

  1. 注册和注销死亡回调:

一旦 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);
}

 

    1.  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;
}

 

  1. 基本概念和流程:

正向的消亡通知:当Stub对象在服务端消亡时,Proxy对象在客户端可以感知到这一事件。

反向的死消亡通知:希望Stub在服务端可以感知到Proxy在客户端的消亡状态。

  1. 实现方法:

假设有两个进程 A 和 B:进程 A是Stub所在进程,负责处理客户端的请求。进程 B是Proxy所在进程,与进程 A 进行通信。

  1. 进程 B 获取进程 A 的 Proxy 对象:
  2. 创建匿名Stub对象:在进程 B 中,创建一个匿名的Stub对象,即一个未向 System Ability Manager (SAMgr) 注册的Stub对象。这个Stub对象是为了实现反向通知而存在的。
  3. 传递匿名Stub给进程 A 的原Stub:进程 B 将这个匿名Stub对象通过 SendRequest 等接口传递给进程 A 的原Stub对象。这可以通过网络传输,具体的实现依赖于通信的协议和框架。
  4. 进程 A 接收到进程 B 传递过来的匿名Stub对象,并将其持有。
  5. 匿名Stub的消亡感知:当进程 B 消亡或者设备离开组网时,匿名Stub对象在进程 B 中会被系统回收。
  6. 原Stub感知匿名Stub的消亡:

原Stub在进程 A 中设置了对匿名Stub对象的死亡监听器(Death Recipient)。当匿名Stub对象在进程 B 中消亡时,这个死亡监听器会被触发。原Stub收到这个通知后,就可以处理相关的逻辑,比如清理资源或者发出警告通知。

  1. 注意事项:

跨进程通信限制:这种技术仅限于设备内部的进程间通信,因为匿名Stub对象并未在系统整体的服务管理器(SAMgr)中注册,无法跨设备进行通信。

匿名Stub对象的自动回收:如果匿名Stub对象在进程 B 中没有被任何Proxy对象指向,系统会自动回收它。这种情况下,进程 A 的原Stub将不会收到消亡通知。

  1. 代码解释

利用匿名Stub实现Stub感知Proxy消亡的机制。

  1. Proxy 端代码 (TestAbilityProxy) TestAnonymousStub 方法:

创建了一个 MessageParcel 对象 dataParcel,并调用 UpdateDataVersion 方法来标记其为远程传输版本。

使用 dataParcel.WriteRemoteObject(new TestAbilityStub()) 将一个新的 TestAbilityStub 对象写入 dataParcel 中。这个对象即为匿名Stub,因为它没有向 System Ability Manager (SAMgr) 注册。

调用 Remote()-&gt;SendRequest 方法,将 dataParcel 发送给远程对象,通过指定的 TRANS_ID_REVERSED_MONITOR 标识符进行通信。

最终根据返回结果判断通信是否成功,并返回相应的结果。

  1. Stub 端代码 (TestAbilityStub) OnRemoteRequest 方法:

当收到 TRANS_ID_REVERSED_MONITOR 标识符的请求时,从 data 中读取 IRemoteObject 对象 obj。

检查 obj 是否为 nullptr,如果是,则返回错误代码 ERR_NULL_OBJECT。

否则,调用 obj-&gt;AddDeathRecipient(new TestDeathRecipient()),将一个 TestDeathRecipient 对象注册为 obj 的死亡监听器(Death Recipient)。

最后根据操作结果,向 reply 中写入相应的错误代码或成功代码。

  1. IPC通信开发指导(C/C++)

    1. 场景介绍

  1. IPC的主要作用:

IPC的主要目的是让不同的进程(或者线程)能够在运行中相互通信和交换数据。这些进程可能运行在同一台计算机的不同内存空间中,或者分布在网络上的不同计算节点上。

在软件设计中,通常会有两种角色来实现IPC:Proxy(代理)和Stub(存根)。Proxy位于一个进程中,而Stub则位于另一个进程中。它们通过一定的IPC机制来进行通信,例如通过网络、共享内存、消息队列等方式。

  1. IPC CAPI接口:

IPC CAPI接口通常是指为了在C语言环境下实现IPC而提供的接口或库。这些接口提供了一种方式来处理进程间通信的底层细节,例如建立通道、发送和接收消息等。

与高级语言不同,IPC CAPI接口通常较为底层,需要开发人员显式地管理内存分配、消息格式等细节。

  1. 元能力(Meta-ability)的作用:

元能力可能包括了对通信协议的封装、安全性、错误处理、性能优化等方面的支持,使得开发者可以更专注于业务逻辑而不必关注底层的细节。

    1.  接口说明

表1  CAPI侧IPC接口:

    1.  开发步骤

      1. 添加动态链接库
# ipc capi
libipc_capi.so
# 元能力,ability capi
libchild_process.so
      1. 头文件

 

// ipc capi
#include <IPCKit/ipc_kit.h>
// 元能力,ability capi
#include <AbilityKit/native_child_process.h>
      1. 异步调用场景
  1. 公共数据及函数定义
#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;
}
  1. 头文件:<string> 提供了 C++ 的字符串类支持,<thread> 和 <mutex> 提供了线程和互斥锁的支持,<chrono> 提供了时间支持,<condition_variable> 提供了条件变量的支持;<IPCKit/ipc_kit.h> 和 <AbilityKit/native_child_process.h> 可能是项目特定的库文件,用于处理 IPC 和子进程相关的功能;<hilog/log.h> 是一个日志记录的库文件。
  2. 宏定义:LOG_DOMAIN 和 LOG_TAG,用于日志记录。LOG_DOMAIN 设置为 0x0201,可能用于区分日志域。LOG_TAG 设置为 "IPCCApiSample",用于标识日志的标签。
  3. 枚举类型: RequestCode,包括三个枚举值:ASYNC_ADD_CODE 和 REQUEST_EXIT_CODE 分别设置为 1 和 2,OTHER_CODE 没有显式赋值,默认值为 3。
  4. 常量定义:MAX_MEMORY_SIZE 设置为 204800,定义了一个最大内存大小的常量;INTERFACE_DESCRIPTOR、NATIVE_REMOTE_STUB_TEST_TOKEN 和 NATIVE_REMOTE_STUB_ASYNC_CALL_TEST_TOKEN 都是字符串常量,用于标识接口描述符和测试用的远程存根 token。
  5. 函数定义:LocalMemoryAllocator 是一个静态函数,用于分配指定长度的内存。如果长度超出 MAX_MEMORY_SIZE 的范围,则返回 nullptr。否则,使用 malloc 分配内存,然后将其清零并返回分配的内存指针。
  1. 服务端对象: 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;
}

 

  1. 类定义:

IpcCApiStubTest 类声明了几个公共方法和静态方法,以及一些私有成员变量。公共方法包括构造函数、析构函数、MainProc 方法、GetRemoteStub 方法和静态的 OnRemoteRequest 方法。私有方法包括 AsyncAdd 方法和 RequestExitChildProcess 方法。类中还包含了一个指向 OHIPCRemoteStub 对象的指针 stub_,以及用于线程同步的 std::mutex 和 std::condition_variable。

  1. 构造函数和析构函数:

构造函数 IpcCApiStubTest() 是显式声明的构造函数,初始化了 stub_ 对象,调用 OH_IPCRemoteStub_Create 函数创建 IPC 远程存根对象,并注册了类的静态成员函数 OnRemoteRequest 作为 IPC 请求处理的回调函数。

析构函数 ~IpcCApiStubTest() 释放了 stub_ 对象,通过调用 OH_IPCRemoteStub_Destroy 函数。

  1. MainProc 方法:

MainProc 方法是一个公共方法,使用 std::unique_lock<std::mutex> 对 childMutex_ 上锁,并调用 childCondVar_.wait(autoLock) 使当前线程等待。这通常用于线程间的条件变量同步,直到收到通知(notify)才继续执行。

  1. GetRemoteStub 方法:

GetRemoteStub 方法返回 stub_ 成员变量,即当前的 IPC 远程存根对象的指针。

  1. OnRemoteRequest 静态方法:

OnRemoteRequest 是一个静态方法,用于处理远程 IPC 请求。它首先进行接口校验,验证传入的 data 中的接口令牌,并根据请求的 code 调用相应的成员方法 AsyncAdd 或 RequestExitChildProcess。

  1. AsyncAdd 方法:

AsyncAdd 方法是一个私有方法,处理异步加法操作。它从 data 中读取两个整数,然后创建一个新的线程执行加法操作,并通过 IPC 通信返回结果。

  1. RequestExitChildProcess 方法:

RequestExitChildProcess 方法是一个私有方法,用于通知子进程退出。它通过 childCondVar_.notify_one() 通知正在等待的线程,即 MainProc 方法中的等待将被唤醒,以便退出子进程。

  1. AsyncAdd 方法:

这个方法用于执行异步加法操作。首先,它从传入的 data 参数中读取两个整数 a 和 b。然后,它获取一个远程代理回调对象 proxyCallBack,如果无法获取或者读取数据失败,方法会返回错误码。

接下来,它创建一个新的线程,线程内部进行加法计算并将结果写入 OH_IPCParcel 对象中。然后,使用 OH_IPCRemoteProxy_SendRequest 方法将数据发送给远程代理,并等待处理结果。

最后,线程结束并分离,方法返回异步操作的成功状态。

  1. RequestExitChildProcess 方法:

这个方法用于通知子进程退出。它使用互斥锁 childMutex_ 和条件变量 childCondVar_ 来确保线程安全。

方法内部调用 notify_all() 方法唤醒所有等待在 childCondVar_ 上的线程,通知它们子进程可以退出。

最后,方法返回表示成功的状态码。

  1. 客户端代理对象: 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!");
}

 

下面是对代码的解释:

  1. 构造函数 IpcCApiProxyTest::IpcCApiProxyTest:

构造函数接受一个 OHIPCRemoteProxy 类型的指针作为参数,用于初始化类的成员变量。如果传入的 proxy 参数为空指针,则记录错误日志并返回。创建一个 replyStub_ 对象,用于处理远程请求的响应。创建一个 deathRecipient_ 对象,用于处理远程代理对象销毁时的回调。将 deathRecipient_ 注册到 proxy_ 上,以便在代理销毁时执行特定操作。

  1. 析构函数 IpcCApiProxyTest::~IpcCApiProxyTest:

析构函数负责释放类中使用的资源,包括 proxy_、deathRecipient_ 和 replyStub_。.检查每个资源是否为空,如果不为空则调用相应的销毁函数进行资源释放。

  1. IpcCApiProxyTest::AsyncAdd:

用于向远程代理发送异步加法请求。

首先创建一个 OHIPCParcel 对象 data,用于封装要发送的数据。写入接口校验token和需要传递的整型参数 a 和 b 到 data 中。使用 replyStub_ 写入远程调用所需的信息。设置 OH_IPC_MessageOption 结构体,指定请求为异步模式,并调用 OH_IPCRemoteProxy_SendRequest 函数发送请求。如果发送请求失败,则记录错误并返回相应的错误码。使用 WaitForAsyncReply 函数等待异步响应,然后将响应结果写入 result 变量中。最后销毁 data 对象,并返回成功状态码 OH_IPC_SUCCESS。

  1.  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。

  1. void IpcCApiProxyTest::SendAsyncReply(int &amp;replyValue)

用于向异步请求的调用者发送回复。

使用 std::unique_lock&lt;std::mutex&gt; 锁住 mutex_,确保线程安全。将传入的 replyValue 赋值给 asyncReply_,表示异步操作的结果。调用 cv_.notify_all() 通知所有等待的线程,异步回复已经准备好。

  1. int IpcCApiProxyTest::WaitForAsyncReply(int timeOut)

用于等待异步回复的到来。

首先将 asyncReply_ 置为0,表示当前没有接收到任何回复。使用 std::unique_lock&lt;std::mutex&gt; 锁住 mutex_,确保线程安全。调用 cv_.wait_for() 等待 timeOut 秒,直到收到 cv_.notify_all() 通知或超时。Lambda 表达式 [] { return asyncReply_ != 0; } 用于判断是否收到回复。返回收到的 asyncReply_ 值。

  1. 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-&gt;SendAsyncReply(val) 向调用者发送异步回复。返回 OH_IPC_SUCCESS 表示处理成功。

  1. void IpcCApiProxyTest::ClearResource()

用于清理资源,是在远程代理对象销毁时调用的回调函数。

  1. void IpcCApiProxyTest::OnDeathRecipientCB(void *userData)

这个静态成员函数是远程代理对象销毁时的回调函数。将 userData 转换为 IpcCApiProxyTest* 类型的指针 proxyTest。如果 proxyTest 不为空,则调用 proxyTest-&gt;ClearResource() 清理资源。记录日志表明远程代理对象已经销毁。

  1. 服务端调用入口,服务端文件"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

 

  1. 全局对象声明:IpcCApiStubTest g_ipcStubObj;

定义了一个全局对象 g_ipcStubObj,这个对象的类型为 IpcCApiStubTest。它被用来管理和处理IPC通信的相关功能。

  1. extern "C" 块:

使用 extern "C" 声明,这段代码表明以下内容是使用C语言的链接规则编写的,以便能够被其他编程语言或环境调用。

  1. NativeChildProcess_OnConnect 函数:

NativeChildProcess_OnConnect 函数在子进程连接时被调用。它记录一个信息日志,然后通过 g_ipcStubObj 对象调用 GetRemoteStub() 方法,获取一个远程通信的Stub对象,并将其返回。

  1. NativeChildProcess_MainProc 函数:

NativeChildProcess_MainProc 函数定义了子进程的主处理过程。它首先记录一个信息日志,然后通过 g_ipcStubObj 对象调用 MainProc() 方法执行主处理逻辑,最后再次记录一个信息日志表示主处理过程结束。

  1. 客户端调用入口

该利用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;

}

 

  1. OnNativeChildProcessStarted 函数:

当子进程启动后,该函数被调用。它接收一个错误码 errCode 和一个 OHIPCRemoteProxy 对象 remoteProxy 作为参数。函数首先记录启动信息的日志,然后检查 remoteProxy 是否为 nullptr。如果是 nullptr,说明创建IPC代理失败或子进程启动失败,函数直接返回。否则,它尝试创建一个 IpcCApiProxyTest 对象 g_ipcProxy,该对象用于管理和调用 remoteProxy 提供的功能。

  1. main 函数:

main 函数首先调用 OH_Ability_CreateNativeChildProcess 函数来创建一个本地子进程,传入动态链接库的名称和 OnNativeChildProcessStarted 作为回调函数。

如果创建子进程的返回值 ret 不为 0,说明创建失败,程序返回 -1。如果 g_ipcProxy 仍然为 nullptr,也即子进程启动后未成功创建 g_ipcProxy 对象,程序同样返回 -1。

通过 g_ipcProxy 调用子进程中的函数 AsyncAdd 进行异步加法操作,并记录操作的结果和返回值。通过调用 RequestExitChildProcess 方法向子进程发送终止请求。程序删除 g_ipcProxy 对象并置其为 nullptr,确保资源正确释放。

  • 40
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值