基本概念
IPC(Inter-Process Communication)与RPC(Remote Procedure Call)用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,后者使用软总线驱动,用于跨设备跨进程通信。需要跨进程通信的原因是因为每个进程都有自己独立的资源和内存空间,其他进程不能随意访问不同进程的内存和资源,IPC/RPC便是为了突破这一点。
说明
Stage模型不能直接使用本文介绍的IPC和RPC,需要通过以下能力实现相关业务场景:
- IPC典型使用场景在后台服务,应用的后台服务通过IPC机制提供跨进程的服务调用能力。
- RPC典型使用场景在多端协同,多端协同通过RPC机制提供远端接口调用与数据传递能力。
实现原理
IPC和RPC通常采用客户端-服务器(Client-Server)模型,在使用时,请求服务的(Client)一端进程可获取提供服务(Server)一端所在进程的代理(Proxy),并通过此代理读写数据来实现进程间的数据通信,更具体的讲,首先请求服务的(Client)一端会建立一个服务提供端(Server)的代理对象,这个代理对象具备和服务提供端(Server)一样的功能,若想访问服务提供端(Server)中的某一个方法,只需访问代理对象中对应的方法即可,代理对象会将请求发送给服务提供端(Server);然后服务提供端(Server)处理接受到的请求,处理完之后通过驱动返回处理结果给代理对象;最后代理对象将请求结果进一步返回给请求服务端(Client)。通常,Server会先注册系统能力(System Ability)到系统能力管理者(System Ability Manager,缩写SAMgr)中,SAMgr负责管理这些SA并向Client提供相关的接口。Client要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理,然后使用代理和SA通信。下文直接使用Proxy表示服务请求方,Stub表示服务提供方。
约束与限制
-
单个设备上跨进程通信时,传输的数据量最大约为1MB,过大的数据量请使用匿名共享内存 。
-
不支持在RPC中订阅匿名Stub对象(没有向SAMgr注册Stub对象)的死亡通知。
-
不支持把跨设备的Proxy对象传递回该Proxy对象所指向的Stub对象所在的设备,即指向远端设备Stub的Proxy对象不能在本设备内进行二次跨进程传递。
IPC与RPC通信开发指南
场景介绍
IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,包括Proxy和Stub运行在不同设备的情况。
开发步骤
ArkTS侧开发步骤
说明
- 此文档中的示例代码描述的是系统应用跨进程通信。
- 当前不支持三方应用实现ServiceExtensionAbility,三方应用的UIAbility组件可以通过Context连接系统提供的ServiceExtensionAbility。
- 当前使用场景: 仅限客户端是三方应用,服务端是系统应用。
- 添加依赖
// FA模型需要从@kit.AbilityKit导入featureAbility
// import { featureAbility } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';
- 绑定Ability
首先,构造变量want,指定要绑定的Ability所在应用的包名、组件名,如果是跨设备的场景,还需要绑定目标设备NetworkId(组网场景下对应设备的标识符,可以使用distributedDeviceManager获取目标设备的NetworkId);然后,构造变量connect,指定绑定成功、绑定失败、断开连接时的回调函数;最后,FA模型使用featureAbility提供的接口绑定Ability,Stage模型通过context获取服务后用提供的接口绑定Ability。
// 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);
}
- 服务端处理客户端请求
服务端被绑定的Ability在onConnect方法里返回继承自 rpc.RemoteObject 的对象,该对象需要实现 onRemoteMessageRequest 方法,处理客户端的请求。
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;
}
}
- 客户端处理服务端响应
客户端在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);
}
- 断开连接
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);
远端状态订阅开发实例
IPC/RPC提供对远端Stub对象状态的订阅机制,在远端Stub对象消亡时,可触发消亡通知告诉本地Proxy对象。这种状态通知订阅需要调用特定接口完成,当不再需要订阅时也需要调用特定接口取消。使用这种订阅机制的用户,需要实现消亡通知接口DeathRecipient并实现onRemoteDied方法清理资源。该方法会在远端Stub对象所在进程消亡或所在设备离开组网时被回调。值得注意的是,调用这些接口有一定的顺序。首先,需要Proxy订阅Stub消亡通知,若在订阅期间Stub状态正常,则在不再需要时取消订阅;若在订阅期间Stub所在进程退出或者所在设备退出组网,则会自动触发Proxy自定义的后续操作。
使用场景
这种订阅机制适用于本地Proxy对象需要感知远端Stub对象所在进程消亡,或所在设备离开组网的场景。当Proxy感知到Stub端消亡后,可适当清理本地资源。此外,RPC目前不提供匿名Stub对象的消亡通知,即只有向SAMgr注册过的服务才能被订阅消亡通知,IPC则支持匿名对象的消亡通知。
ArkTS侧接口
说明
-
此文档中的示例代码描述的是系统应用跨进程通信。
-
当前不支持三方应用实现ServiceExtensionAbility,三方应用的UIAbility组件可以通过Context连接系统提供的ServiceExtensionAbility。
-
当前使用场景: 仅限客户端是三方应用,服务端是系统应用。
接口名 | 返回值类型 | 功能描述 |
---|---|---|
registerDeathRecipient | void | 注册用于接收远程对象消亡通知的回调,增加 proxy 对象上的消亡通知。 |
unregisterDeathRecipient | void | 注销用于接收远程对象消亡通知的回调。 |
onRemoteDied | void | 在成功添加死亡通知订阅后,当远端对象死亡时,将自动调用本方法。 |
参考代码
// 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);
上述onConnect回调函数中的proxy对象需要等ability异步连接成功后才会被赋值,然后才可调用proxy对象的 unregisterDeathRecipient 接口方法注销死亡回调
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的使用)
正向的消亡通知是Proxy感知Stub的状态,若想达到反向的死消亡通知,即Stub感知Proxy的状态,可以巧妙的利用正向消亡通知。如两个进程A(原Stub所在进程)和B(原Proxy所在进程),进程B在获取到进程A的Proxy对象后,在B进程新建一个匿名Stub对象(匿名指未向SAMgr注册),可称之为回调Stub,再通过SendRequest接口将回调Stub传给进程A的原Stub。这样一来,进程A便获取到了进程B的回调Proxy。当进程B消亡或B所在设备离开组网时,回调Stub会消亡,回调Proxy会感知,进而通知给原Stub,便实现了反向消亡通知。
注意:
- 反向死亡通知仅限设备内跨进程通信使用,不可用于跨设备。
- 当匿名Stub对象没有被任何一个Proxy指向的时候,内核会自动回收。
参考代码
// 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;
}
IPC通信开发指导(C/C++)
场景介绍
IPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,而IPC CAPI是提供的C接口。
IPC CAPI接口不直接提供跨进程通信能力,两个进程之间的IPC通道建立,依赖于元能力。
进程间IPC通道建立,详情参考 元能力CAPI接口开发指导 ,本文重点阐述IPC CAPI部分使用说明。
接口说明
表1 CAPI侧IPC接口
接口名 | 描述 |
---|---|
typedef int (*OH_OnRemoteRequestCallback) (uint32_t code, const OHIPCParcel *data, OHIPCParcel *reply, void *userData); | Stub端用于处理远端数据请求的回调函数 |
OHIPCRemoteStub* OH_IPCRemoteStub_Create (const char *descriptor, OH_OnRemoteRequestCallback requestCallback, OH_OnRemoteDestroyCallback destroyCallback, void *userData); | 创建OHIPCRemoteStub对象 |
int OH_IPCRemoteProxy_SendRequest(const OHIPCRemoteProxy *proxy, uint32_t code, const OHIPCParcel *data, OHIPCParcel *reply, const OH_IPC_MessageOption *option); | IPC消息发送函数 |
struct OHIPCRemoteProxy; | OHIPCRemoteProxy对象,用于向远端发送请求。 需要依赖元能力接口返回。 |
OHIPCDeathRecipient* OH_IPCDeathRecipient_Create (OH_OnDeathRecipientCallback deathRecipientCallback,OH_OnDeathRecipientDestroyCallback destroyCallback, void *userData); | 创建远端OHIPCRemoteStub对象死亡 通知对象OHIPCDeathRecipient |
int OH_IPCRemoteProxy_AddDeathRecipient(OHIPCRemoteProxy *proxy,OHIPCDeathRecipient *recipient); | 向OHIPCRemoteProxy对象添加死亡监听,用于接收远端OHIPCRemoteStub对象死亡的回调通知 |
开发步骤
以下步骤描述了如何使用IPCKit提供的CAPI接口,创建远端Stub和使用客户端代理Proxy进行通信,同时兼备远端死亡通知接收能力。
1. 添加动态链接库
CMakeLists.txt中添加以下lib。
# ipc capi
libipc_capi.so
# 元能力,ability capi
libchild_process.so
2. 头文件
// ipc capi
#include <IPCKit/ipc_kit.h>
// 元能力,ability capi
#include <AbilityKit/native_child_process.h>
3. 异步调用场景
3.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;
}
3.2 服务端对象: IpcCApiStubTest
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;
}
3.3 客户端代理对象: IpcCApiProxyTest
// 用戶自定义错误码
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!");
}
3.4 服务端调用入口,服务端文件"libipcCapiDemo.so"
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
3.5 客户端调用入口
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;
}
鸿蒙全栈开发全新学习指南
之前总有很多小伙伴向我反馈说,不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以这里为大家准备了一份实用的鸿蒙(HarmonyOS NEXT)学习路线与学习文档用来跟着学习是非常有必要的。
针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。
本路线共分为四个阶段:
第一阶段:鸿蒙初中级开发必备技能
第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH
第三阶段:应用开发中高级就业技术
第四阶段:全网首发-工业级南向设备开发就业技术:gitee.com/MNxiaona/733GH
《鸿蒙 (Harmony OS)开发学习手册》(共计892页)
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:gitee.com/MNxiaona/733GH
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
基于ArkTS 开发
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH
鸿蒙入门教学视频:
美团APP实战开发教学:gitee.com/MNxiaona/733GH
写在最后
- 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
- 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:
gitee.com/MNxiaona/733GH