0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

HarmonyOS跨进程通信—IPC与RPC通信开发

王程 来源: jf_75796907 作者: jf_75796907 2024-02-02 17:47 次阅读

一、IPC与RPC通信概述

基本概念

IPC(Inter-Process Communication)与RPC(Remote Procedure Call)用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,后者使用软总线驱动,用于跨设备跨进程通信。需要跨进程通信的原因是因为每个进程都有自己独立的资源和内存空间,其他进程不能随意访问不同进程的内存和资源,IPC/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表示服务提供方。

wKgaomW8uiqAZmEVAAAZ1j7EkEI957.pngwKgZomUT3jGAEdeHAAAZ1j7EkEI286.png

约束与限制

​ ● 单个设备上跨进程通信时,传输的数据量最大约为1MB,过大的数据量请使用匿名共享内存

​ ● 不支持在RPC中订阅匿名Stub对象(没有向SAMgr注册Stub对象)的死亡通知。

​ ● 不支持把跨设备的Proxy对象传递回该Proxy对象所指向的Stub对象所在的设备,即指向远端设备Stub的Proxy对象不能在本设备内进行二次跨进程传递。

使用建议

首先,需要编写接口类,接口类中必须定义消息码,供通信双方标识操作,可以有未实现的的方法,因为通信双方均需继承该接口类且双方不能是抽象类,所以此时定义的未实现的方法必须在双方继承时给出实现,这保证了继承双方不是抽象类。然后,需要编写Stub端相关类及其接口,并且实现AsObject方法及OnRemoteRequest方法。同时,也需要编写Proxy端,实现接口类中的方法和AsObject方法,也可以封装一些额外的方法用于调用SendRequest向对端发送数据。以上三者都具备后,便可以向SAMgr注册SA了,此时的注册应该在Stub所在进程完成。最后,在需要的地方从SAMgr中获取Proxy,便可通过Proxy实现与Stub的跨进程通信了。

相关步骤:

​ ● 实现接口类:需继承IRemoteBroker,需定义消息码,可声明不在此类实现的方法。

​ ● 实现服务提供端(Stub):需继承IRemoteStub或者RemoteObject,需重写AsObject方法及OnRemoteRequest方法。

​ ● 实现服务请求端(Proxy):需继承IRemoteProxy或RemoteProxy,需重写AsObject方法,封装所需方法调用SendRequest。

​ ● 注册SA:申请SA的唯一ID,向SAMgr注册SA。

​ ● 获取SA:通过SA的ID和设备ID获取Proxy,使用Proxy与远端通信

二、 IPC与RPC通信开发指导

场景介绍

IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,包括Proxy和Stub运行在不同设备的情况。

接口说明

表1 Native侧IPC接口

类/接口 方法 功能说明
IRemoteBroker sptr AsObject() 返回通信对象。Stub端返回RemoteObject对象本身,Proxy端返回代理对象。
IRemoteStub virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) 请求处理方法,派生类需要重写该方法用来处理Proxy的请求并返回结果。
IRemoteProxy Remote()->SendRequest(code, data, reply, option) 消息发送方法,业务的Proxy类需要从IRemoteProxy类派生,该方法用来向对端发送消息。

开发步骤

Native侧开发步骤

​ 1. 添加依赖

SDK依赖:

#ipc场景
external_deps = [
  "ipc:ipc_single",
]

#rpc场景
external_deps = [
  "ipc:ipc_core",
]

此外, IPC/RPC依赖的refbase实现在公共基础库下,请增加对utils的依赖:

external_deps = [
  "c_utils:utils",
]

2.定义IPC接口ITestAbility

SA接口继承IPC基类接口IRemoteBroker,接口里定义描述符、业务函数和消息码,其中业务函数在Proxy端和Stub端都需要实现。

#include "iremote_broker.h"

//定义消息码
const int TRANS_ID_PING_ABILITY = 5

const std::string DESCRIPTOR = "test.ITestAbility";

class ITestAbility : public IRemoteBroker {
public:
    // DECLARE_INTERFACE_DESCRIPTOR是必需的,入参需使用std::u16string;
    DECLARE_INTERFACE_DESCRIPTOR(to_utf16(DESCRIPTOR));
    virtual int TestPingAbility(const std::u16string &dummy) = 0; // 定义业务函数
};

3.定义和实现服务端TestAbilityStub

该类是和IPC框架相关的实现,需要继承 IRemoteStub。Stub端作为接收请求的一端,需重写OnRemoteRequest方法用于接收客户端调用。

#include "iability_test.h"
#include "iremote_stub.h"

class TestAbilityStub : public IRemoteStub< ITestAbility > {
public:
    virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override;
    int TestPingAbility(const std::u16string &dummy) override;
 };

int TestAbilityStub::OnRemoteRequest(uint32_t code,
    MessageParcel &data, MessageParcel &reply, MessageOption &option)
{
    switch (code) {
        case TRANS_ID_PING_ABILITY: {
            std::u16string dummy = data.ReadString16();
            int result = TestPingAbility(dummy);
            reply.WriteInt32(result);
            return 0;
        }
        default:
            return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
    }
}

​ 4. 定义服务端业务函数具体实现类TestAbility

#include "iability_server_test.h"

class TestAbility : public TestAbilityStub {
public:
    int TestPingAbility(const std::u16string &dummy);
}

int TestAbility::TestPingAbility(const std::u16string &dummy) {
    return 0;
}

​ 5. 定义和实现客户端 TestAbilityProxy

该类是Proxy端实现,继承IRemoteProxy,调用SendRequest接口向Stub端发送请求,对外暴露服务端提供的能力。


#include "iability_test.h"
#include "iremote_proxy.h"
#include "iremote_object.h"

class TestAbilityProxy : public IRemoteProxy< ITestAbility > {
public:
    explicit TestAbilityProxy(const sptr< IRemoteObject > &impl);
    int TestPingAbility(const std::u16string &dummy) override;
private:
    static inline BrokerDelegator< TestAbilityProxy > delegator_; // 方便后续使用iface_cast宏
}

TestAbilityProxy::TestAbilityProxy(const sptr< IRemoteObject > &impl)
    : IRemoteProxy< ITestAbility >(impl)
{
}

int TestAbilityProxy::TestPingAbility(const std::u16string &dummy){
    MessageOption option;
    MessageParcel dataParcel, replyParcel;
    dataParcel.WriteString16(dummy);
    int error = Remote()->SendRequest(TRANS_ID_PING_ABILITY, dataParcel, replyParcel, option);
    int result = (error == ERR_NONE) ? replyParcel.ReadInt32() : -1;
    return result;
}

​ 6. SA注册与启动

SA需要将自己的TestAbilityStub实例通过AddSystemAbility接口注册到SystemAbilityManager,设备内与分布式的注册参数不同。

// 注册到本设备内
auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
samgr->AddSystemAbility(saId, new TestAbility());

// 在组网场景下,会被同步到其他设备上
auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
ISystemAbilityManager::SAExtraProp saExtra;
saExtra.isDistributed = true; // 设置为分布式SA
int result = samgr->AddSystemAbility(saId, new TestAbility(), saExtra);

​ 7. SA获取与调用

通过SystemAbilityManager的GetSystemAbility方法可获取到对应SA的代理IRemoteObject,然后构造TestAbilityProxy即可。

// 获取本设备内注册的SA的proxy
sptr< ISystemAbilityManager > samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
sptr< IRemoteObject > remoteObject = samgr->GetSystemAbility(saId);
sptr< ITestAbility > testAbility = iface_cast< ITestAbility >(remoteObject); // 使用iface_cast宏转换成具体类型

// 获取其他设备注册的SA的proxy
sptr< ISystemAbilityManager > samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();

// networkId是组网场景下对应设备的标识符,可以通过GetLocalNodeDeviceInfo获取
sptr< IRemoteObject > remoteObject = samgr->GetSystemAbility(saId, networkId);
sptr< TestAbilityProxy > proxy(new TestAbilityProxy(remoteObject)); // 直接构造具体Proxy

JS侧开发步骤

​ 1. 添加依赖

import rpc from "@ohos.rpc"
import featureAbility from "@ohos.ability.featureAbility"

2.绑定Ability

首先,构造变量want,指定要绑定的Ability所在应用的包名、组件名,如果是跨设备的场景,还需要绑定目标设备NetworkId(组网场景下对应设备的标识符,可以使用deviceManager获取目标设备的NetworkId);然后,构造变量connect,指定绑定成功、绑定失败、断开连接时的回调函数;最后,使用featureAbility提供的接口绑定Ability。

import rpc from "@ohos.rpc"
import featureAbility from "@ohos.ability.featureAbility"

let proxy = null
let connectId = null

// 单个设备绑定Ability
let want = {
    // 包名和组件名写实际的值
    "bundleName": "ohos.rpc.test.server",
    "abilityName": "ohos.rpc.test.server.ServiceAbility",
}
let connect = {
    onConnect:function(elementName, remote) {
        proxy = remote
    },
    onDisconnect:function(elementName) {
    },
    onFailed:function() {
        proxy = null
    }
}
connectId = featureAbility.connectAbility(want, connect)

// 如果是跨设备绑定,可以使用deviceManager获取目标设备NetworkId
import deviceManager from '@ohos.distributedHardware.deviceManager'
function deviceManagerCallback(deviceManager) {
    let deviceList = deviceManager.getTrustedDeviceListSync()
    let networkId = deviceList[0].networkId
    let want = {
        "bundleName": "ohos.rpc.test.server",
        "abilityName": "ohos.rpc.test.service.ServiceAbility",
        "networkId": networkId,
        "flags": 256
    }
    connectId = featureAbility.connectAbility(want, connect)
}
// 第一个参数是本应用的包名,第二个参数是接收deviceManager的回调函数
deviceManager.createDeviceManager("ohos.rpc.test", deviceManagerCallback)

3.服务端处理客户端请求

服务端被绑定的Ability在onConnect方法里返回继承自rpc.RemoteObject的对象,该对象需要实现onRemoteMessageRequest方法,处理客户端的请求。

onConnect(want: Want) {
    var robj:rpc.RemoteObject = new Stub("rpcTestAbility")
    return robj
}
class Stub extends rpc.RemoteObject {
    constructor(descriptor) {
        super(descriptor)
    }
    onRemoteMessageRequest(code, data, reply, option) {
        // 根据code处理客户端的请求
        return true
    }
}

4.客户端处理服务端响应

客户端在onConnect回调里接收到代理对象,调用sendRequestAsync方法发起请求,在期约(JavaScript期约:用于表示一个异步操作的最终完成或失败及其结果值)或者回调函数里接收结果。

// 使用期约
let option = new rpc.MessageOption()
let data = rpc.MessageParcel.create()
let reply = rpc.MessageParcel.create()
// 往data里写入参数
proxy.sendRequestAsync(1, data, reply, option)
    .then(function(result) {
        if (result.errCode != 0) {
            console.error("send request failed, errCode: " + result.errCode)
            return
        }
        // 从result.reply里读取结果
    })
    .catch(function(e) {
        console.error("send request got exception: " + e)
    }
    .finally(() => {
        data.reclaim()
        reply.reclaim()
    })

// 使用回调函数
function sendRequestCallback(result) {
    try {
        if (result.errCode != 0) {
            console.error("send request failed, errCode: " + result.errCode)
            return
        }
        // 从result.reply里读取结果
    } finally {
        result.data.reclaim()
        result.reply.reclaim()
    }
}
let option = new rpc.MessageOption()
let data = rpc.MessageParcel.create()
let reply = rpc.MessageParcel.create()
// 往data里写入参数
proxy.sendRequest(1, data, reply, option, sendRequestCallback)

5.断开连接

IPC通信结束后,使用featureAbility的接口断开连接。

import rpc from "@ohos.rpc"
import featureAbility from "@ohos.ability.featureAbility"
function disconnectCallback() {
    console.info("disconnect ability done")
}
featureAbility.disconnectAbility(connectId, disconnectCallback)


审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • RPC
    RPC
    +关注

    关注

    0

    文章

    111

    浏览量

    11529
  • IPC
    IPC
    +关注

    关注

    3

    文章

    346

    浏览量

    51902
  • 鸿蒙
    +关注

    关注

    57

    文章

    2339

    浏览量

    42805
  • HarmonyOS
    +关注

    关注

    79

    文章

    1973

    浏览量

    30143
收藏 人收藏

    评论

    相关推荐

    鸿蒙OS跨进程IPCRPC通信

    一、IPCRPC通信概述 基本概念 IPC(Inter-Process Communication)与RPC(Remote Procedu
    发表于 02-17 14:20

    linux操作系统下的进程通信设计

    通信方面的侧重点有所不同。前者对Unix早期的进程通信手段进行了系统的改进和扩充,形成了“system V IPC”,通信
    发表于 04-16 09:17

    【Linux学习杂谈】之进程通信

    我们详细看下进程通信大致分为以下几个方面: Linux进程通信的几种机制:(1)无名管道和有名管道(2)system V IPC 信号
    发表于 10-15 14:45

    HarmonyOS教程—基于分布式能力和IDL跨进程通信,实现视频跨设备播放、控制

    。如何使用HarmonyOS IDL跨进程通信实现远程控制视频播放。技能要求HarmonyOS Player接口熟练使用基本组件熟练使用2. 搭建H
    发表于 09-13 11:49

    【中秋国庆不断更】HarmonyOS跨进程通信IPCRPC通信开发指导

    一、IPCRPC通信概述 基本概念 IPC(Inter-Process Communication)与RPC(Remote Procedu
    发表于 09-27 15:48

    linux操作系统下的进程通信

    的侧重点有所不同。前者对Unix早期的进程通信手段进行了系统的改进和扩充,形成了system V IPC通信进程局限在单个计算机内;后者
    发表于 10-31 11:15 0次下载

    Linux系统中的进程之间通信

    六、总结 一、Linux 系统中的进程之间通信IPC)作为一名嵌入式软件开发人员来说,处理进程之间的
    的头像 发表于 04-12 10:06 4628次阅读
    Linux系统中的<b class='flag-5'>进程</b>之间<b class='flag-5'>通信</b>

    Linux进程间的五种通信方式介绍 3

    进程通信IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。IPC的方式通常有管道(包括无名
    的头像 发表于 02-15 10:19 508次阅读

    Linux进程间的五种通信方式介绍 4

    进程通信IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。IPC的方式通常有管道(包括无名
    的头像 发表于 02-15 10:19 592次阅读

    Linux进程间的五种通信方式介绍 6

    进程通信IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。IPC的方式通常有管道(包括无名
    的头像 发表于 02-15 10:19 466次阅读

    Linux进程间的五种通信方式介绍 5

    进程通信IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。IPC的方式通常有管道(包括无名
    的头像 发表于 02-15 10:20 580次阅读

    进程通信的机制有哪些

    进程通信(interprocess communication,简称IPC)指两个进程之间的通信。系统中的每一个
    的头像 发表于 07-21 11:23 926次阅读
    <b class='flag-5'>进程</b>间<b class='flag-5'>通信</b>的机制有哪些

    RPC 和 REST 区别是什么

    01. 既 REST ,何 RPC ? 在 OpenStack 里的进程通信方式主要有两种,一种是基于HTTP协议的RESTFul API方式,另一种则是RPC调用。 那么这两种方式
    的头像 发表于 11-02 10:40 3109次阅读
    <b class='flag-5'>RPC</b> 和 REST 区别是什么

    进程通信方式总结

    进程通信IPC): 进程通信的方式有很多,这里主要讲到进程
    的头像 发表于 11-09 09:25 750次阅读
    <b class='flag-5'>进程</b>间<b class='flag-5'>通信</b>方式总结

    鸿蒙开发通信与连接:ohos.rpc RPC通信

    本模块提供进程通信能力,包括设备内的进程通信IPC)和设备间的进程
    的头像 发表于 06-21 09:40 538次阅读
    鸿蒙<b class='flag-5'>开发</b><b class='flag-5'>通信</b>与连接:ohos.<b class='flag-5'>rpc</b> <b class='flag-5'>RPC</b><b class='flag-5'>通信</b>