0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

HarmonyOS跨進(jìn)程通信—IPC與RPC通信開發(fā)

王程 ? 來源: jf_75796907 ? 作者: jf_75796907 ? 2024-02-02 17:47 ? 次閱讀

一、IPC與RPC通信概述

基本概念

IPC(Inter-Process Communication)與RPC(Remote Procedure Call)用于實(shí)現(xiàn)跨進(jìn)程通信,不同的是前者使用Binder驅(qū)動,用于設(shè)備內(nèi)的跨進(jìn)程通信,后者使用軟總線驅(qū)動,用于跨設(shè)備跨進(jìn)程通信。需要跨進(jìn)程通信的原因是因?yàn)槊總€進(jìn)程都有自己獨(dú)立的資源和內(nèi)存空間,其他進(jìn)程不能隨意訪問不同進(jìn)程的內(nèi)存和資源,IPC/RPC便是為了突破這一點(diǎn)。IPC和RPC通常采用客戶端-服務(wù)器(Client-Server)模型,在使用時,請求服務(wù)的(Client)一端進(jìn)程可獲取提供服務(wù)(Server)一端所在進(jìn)程的代理(Proxy),并通過此代理讀寫數(shù)據(jù)來實(shí)現(xiàn)進(jìn)程間的數(shù)據(jù)通信,更具體的講,首先請求服務(wù)的(Client)一端會建立一個服務(wù)提供端(Server)的代理對象,這個代理對象具備和服務(wù)提供端(Server)一樣的功能,若想訪問服務(wù)提供端(Server)中的某一個方法,只需訪問代理對象中對應(yīng)的方法即可,代理對象會將請求發(fā)送給服務(wù)提供端(Server);然后服務(wù)提供端(Server)處理接受到的請求,處理完之后通過驅(qū)動返回處理結(jié)果給代理對象;最后代理對象將請求結(jié)果進(jìn)一步返回給請求服務(wù)端(Client)。通常,Server會先注冊系統(tǒng)能力(System Ability)到系統(tǒng)能力管理者(System Ability Manager,縮寫SAMgr)中,SAMgr負(fù)責(zé)管理這些SA并向Client提供相關(guān)的接口。Client要和某個具體的SA通信,必須先從SAMgr中獲取該SA的代理,然后使用代理和SA通信。下文直接使用Proxy表示服務(wù)請求方,Stub表示服務(wù)提供方。

wKgaomW8uiqAZmEVAAAZ1j7EkEI957.pngwKgZomUT3jGAEdeHAAAZ1j7EkEI286.png

約束與限制

? ● 單個設(shè)備上跨進(jìn)程通信時,傳輸?shù)臄?shù)據(jù)量最大約為1MB,過大的數(shù)據(jù)量請使用匿名共享內(nèi)存

? ● 不支持在RPC中訂閱匿名Stub對象(沒有向SAMgr注冊Stub對象)的死亡通知。

? ● 不支持把跨設(shè)備的Proxy對象傳遞回該P(yáng)roxy對象所指向的Stub對象所在的設(shè)備,即指向遠(yuǎn)端設(shè)備Stub的Proxy對象不能在本設(shè)備內(nèi)進(jìn)行二次跨進(jìn)程傳遞。

使用建議

首先,需要編寫接口類,接口類中必須定義消息碼,供通信雙方標(biāo)識操作,可以有未實(shí)現(xiàn)的的方法,因?yàn)橥ㄐ烹p方均需繼承該接口類且雙方不能是抽象類,所以此時定義的未實(shí)現(xiàn)的方法必須在雙方繼承時給出實(shí)現(xiàn),這保證了繼承雙方不是抽象類。然后,需要編寫Stub端相關(guān)類及其接口,并且實(shí)現(xiàn)AsObject方法及OnRemoteRequest方法。同時,也需要編寫Proxy端,實(shí)現(xiàn)接口類中的方法和AsObject方法,也可以封裝一些額外的方法用于調(diào)用SendRequest向?qū)Χ税l(fā)送數(shù)據(jù)。以上三者都具備后,便可以向SAMgr注冊SA了,此時的注冊應(yīng)該在Stub所在進(jìn)程完成。最后,在需要的地方從SAMgr中獲取Proxy,便可通過Proxy實(shí)現(xiàn)與Stub的跨進(jìn)程通信了。

相關(guān)步驟:

? ● 實(shí)現(xiàn)接口類:需繼承IRemoteBroker,需定義消息碼,可聲明不在此類實(shí)現(xiàn)的方法。

? ● 實(shí)現(xiàn)服務(wù)提供端(Stub):需繼承IRemoteStub或者RemoteObject,需重寫AsObject方法及OnRemoteRequest方法。

? ● 實(shí)現(xiàn)服務(wù)請求端(Proxy):需繼承IRemoteProxy或RemoteProxy,需重寫AsObject方法,封裝所需方法調(diào)用SendRequest。

? ● 注冊SA:申請SA的唯一ID,向SAMgr注冊SA。

? ● 獲取SA:通過SA的ID和設(shè)備ID獲取Proxy,使用Proxy與遠(yuǎn)端通信

二、 IPC與RPC通信開發(fā)指導(dǎo)

場景介紹

IPC/RPC的主要工作是讓運(yùn)行在不同進(jìn)程的Proxy和Stub互相通信,包括Proxy和Stub運(yùn)行在不同設(shè)備的情況。

接口說明

表1 Native側(cè)IPC接口

類/接口 方法 功能說明
IRemoteBroker sptr AsObject() 返回通信對象。Stub端返回RemoteObject對象本身,Proxy端返回代理對象。
IRemoteStub virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) 請求處理方法,派生類需要重寫該方法用來處理Proxy的請求并返回結(jié)果。
IRemoteProxy Remote()->SendRequest(code, data, reply, option) 消息發(fā)送方法,業(yè)務(wù)的Proxy類需要從IRemoteProxy類派生,該方法用來向?qū)Χ税l(fā)送消息。

開發(fā)步驟

Native側(cè)開發(fā)步驟

? 1. 添加依賴

SDK依賴:

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

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

此外, IPC/RPC依賴的refbase實(shí)現(xiàn)在公共基礎(chǔ)庫下,請增加對utils的依賴:

external_deps = [
  "c_utils:utils",
]

2.定義IPC接口ITestAbility

SA接口繼承IPC基類接口IRemoteBroker,接口里定義描述符、業(yè)務(wù)函數(shù)和消息碼,其中業(yè)務(wù)函數(shù)在Proxy端和Stub端都需要實(shí)現(xiàn)。

#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是必需的,入?yún)⑿枋褂胹td::u16string;
    DECLARE_INTERFACE_DESCRIPTOR(to_utf16(DESCRIPTOR));
    virtual int TestPingAbility(const std::u16string &dummy) = 0; // 定義業(yè)務(wù)函數(shù)
};

3.定義和實(shí)現(xiàn)服務(wù)端TestAbilityStub

該類是和IPC框架相關(guān)的實(shí)現(xiàn),需要繼承 IRemoteStub。Stub端作為接收請求的一端,需重寫OnRemoteRequest方法用于接收客戶端調(diào)用。

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

class TestAbilityStub : public IRemoteStub {
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. 定義服務(wù)端業(yè)務(wù)函數(shù)具體實(shí)現(xiàn)類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. 定義和實(shí)現(xiàn)客戶端 TestAbilityProxy

該類是Proxy端實(shí)現(xiàn),繼承IRemoteProxy,調(diào)用SendRequest接口向Stub端發(fā)送請求,對外暴露服務(wù)端提供的能力。


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

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

TestAbilityProxy::TestAbilityProxy(const sptr &impl)
    : IRemoteProxy(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實(shí)例通過AddSystemAbility接口注冊到SystemAbilityManager,設(shè)備內(nèi)與分布式的注冊參數(shù)不同。

// 注冊到本設(shè)備內(nèi)
auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
samgr->AddSystemAbility(saId, new TestAbility());

// 在組網(wǎng)場景下,會被同步到其他設(shè)備上
auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
ISystemAbilityManager::SAExtraProp saExtra;
saExtra.isDistributed = true; // 設(shè)置為分布式SA
int result = samgr->AddSystemAbility(saId, new TestAbility(), saExtra);

? 7. SA獲取與調(diào)用

通過SystemAbilityManager的GetSystemAbility方法可獲取到對應(yīng)SA的代理IRemoteObject,然后構(gòu)造TestAbilityProxy即可。

// 獲取本設(shè)備內(nèi)注冊的SA的proxy
sptr samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
sptr remoteObject = samgr->GetSystemAbility(saId);
sptr testAbility = iface_cast(remoteObject); // 使用iface_cast宏轉(zhuǎn)換成具體類型

// 獲取其他設(shè)備注冊的SA的proxy
sptr samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();

// networkId是組網(wǎng)場景下對應(yīng)設(shè)備的標(biāo)識符,可以通過GetLocalNodeDeviceInfo獲取
sptr remoteObject = samgr->GetSystemAbility(saId, networkId);
sptr proxy(new TestAbilityProxy(remoteObject)); // 直接構(gòu)造具體Proxy

JS側(cè)開發(fā)步驟

? 1. 添加依賴

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

2.綁定Ability

首先,構(gòu)造變量want,指定要綁定的Ability所在應(yīng)用的包名、組件名,如果是跨設(shè)備的場景,還需要綁定目標(biāo)設(shè)備NetworkId(組網(wǎng)場景下對應(yīng)設(shè)備的標(biāo)識符,可以使用deviceManager獲取目標(biāo)設(shè)備的NetworkId);然后,構(gòu)造變量connect,指定綁定成功、綁定失敗、斷開連接時的回調(diào)函數(shù);最后,使用featureAbility提供的接口綁定Ability。

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

let proxy = null
let connectId = null

// 單個設(shè)備綁定Ability
let want = {
    // 包名和組件名寫實(shí)際的值
    "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)

// 如果是跨設(shè)備綁定,可以使用deviceManager獲取目標(biāo)設(shè)備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)
}
// 第一個參數(shù)是本應(yīng)用的包名,第二個參數(shù)是接收deviceManager的回調(diào)函數(shù)
deviceManager.createDeviceManager("ohos.rpc.test", deviceManagerCallback)

3.服務(wù)端處理客戶端請求

服務(wù)端被綁定的Ability在onConnect方法里返回繼承自rpc.RemoteObject的對象,該對象需要實(shí)現(xiàn)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) {
        // 根據(jù)code處理客戶端的請求
        return true
    }
}

4.客戶端處理服務(wù)端響應(yīng)

客戶端在onConnect回調(diào)里接收到代理對象,調(diào)用sendRequestAsync方法發(fā)起請求,在期約(JavaScript期約:用于表示一個異步操作的最終完成或失敗及其結(jié)果值)或者回調(diào)函數(shù)里接收結(jié)果。

// 使用期約
let option = new rpc.MessageOption()
let data = rpc.MessageParcel.create()
let reply = rpc.MessageParcel.create()
// 往data里寫入?yún)?shù)
proxy.sendRequestAsync(1, data, reply, option)
    .then(function(result) {
        if (result.errCode != 0) {
            console.error("send request failed, errCode: " + result.errCode)
            return
        }
        // 從result.reply里讀取結(jié)果
    })
    .catch(function(e) {
        console.error("send request got exception: " + e)
    }
    .finally(() => {
        data.reclaim()
        reply.reclaim()
    })

// 使用回調(diào)函數(shù)
function sendRequestCallback(result) {
    try {
        if (result.errCode != 0) {
            console.error("send request failed, errCode: " + result.errCode)
            return
        }
        // 從result.reply里讀取結(jié)果
    } finally {
        result.data.reclaim()
        result.reply.reclaim()
    }
}
let option = new rpc.MessageOption()
let data = rpc.MessageParcel.create()
let reply = rpc.MessageParcel.create()
// 往data里寫入?yún)?shù)
proxy.sendRequest(1, data, reply, option, sendRequestCallback)

5.斷開連接

IPC通信結(jié)束后,使用featureAbility的接口斷開連接。

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


審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • RPC
    RPC
    +關(guān)注

    關(guān)注

    0

    文章

    110

    瀏覽量

    11483
  • IPC
    IPC
    +關(guān)注

    關(guān)注

    3

    文章

    335

    瀏覽量

    51712
  • 鴻蒙
    +關(guān)注

    關(guān)注

    57

    文章

    2287

    瀏覽量

    42629
  • HarmonyOS
    +關(guān)注

    關(guān)注

    79

    文章

    1954

    瀏覽量

    29897
收藏 人收藏

    評論

    相關(guān)推薦

    鴻蒙OS跨進(jìn)程IPCRPC通信

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

    linux操作系統(tǒng)下的進(jìn)程通信設(shè)計

    通信方面的側(cè)重點(diǎn)有所不同。前者對Unix早期的進(jìn)程通信手段進(jìn)行了系統(tǒng)的改進(jìn)和擴(kuò)充,形成了“system V IPC”,通信
    發(fā)表于 04-16 09:17

    【Linux學(xué)習(xí)雜談】之進(jìn)程通信

    我們詳細(xì)看下進(jìn)程通信大致分為以下幾個方面: Linux進(jìn)程通信的幾種機(jī)制:(1)無名管道和有名管道(2)system V IPC 信號
    發(fā)表于 10-15 14:45

    HarmonyOS教程—基于分布式能力和IDL跨進(jìn)程通信,實(shí)現(xiàn)視頻跨設(shè)備播放、控制

    。如何使用HarmonyOS IDL跨進(jìn)程通信實(shí)現(xiàn)遠(yuǎn)程控制視頻播放。技能要求HarmonyOS Player接口熟練使用基本組件熟練使用2. 搭建H
    發(fā)表于 09-13 11:49

    【中秋國慶不斷更】HarmonyOS跨進(jìn)程通信IPCRPC通信開發(fā)指導(dǎo)

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

    linux操作系統(tǒng)下的進(jìn)程通信

    的側(cè)重點(diǎn)有所不同。前者對Unix早期的進(jìn)程通信手段進(jìn)行了系統(tǒng)的改進(jìn)和擴(kuò)充,形成了system V IPC,通信進(jìn)程局限在單個計算機(jī)內(nèi);后者
    發(fā)表于 10-31 11:15 ?0次下載

    Linux系統(tǒng)中的進(jìn)程之間通信

    六、總結(jié) 一、Linux 系統(tǒng)中的進(jìn)程之間通信IPC)作為一名嵌入式軟件開發(fā)人員來說,處理進(jìn)程之間的
    的頭像 發(fā)表于 04-12 10:06 ?4565次閱讀
    Linux系統(tǒng)中的<b class='flag-5'>進(jìn)程</b>之間<b class='flag-5'>通信</b>

    Linux進(jìn)程間的五種通信方式介紹 3

    進(jìn)程通信IPC,InterProcess Communication)是指在不同進(jìn)程之間傳播或交換信息。IPC的方式通常有管道(包括無名
    的頭像 發(fā)表于 02-15 10:19 ?466次閱讀

    Linux進(jìn)程間的五種通信方式介紹 4

    進(jìn)程通信IPC,InterProcess Communication)是指在不同進(jìn)程之間傳播或交換信息。IPC的方式通常有管道(包括無名
    的頭像 發(fā)表于 02-15 10:19 ?535次閱讀

    Linux進(jìn)程間的五種通信方式介紹 6

    進(jìn)程通信IPC,InterProcess Communication)是指在不同進(jìn)程之間傳播或交換信息。IPC的方式通常有管道(包括無名
    的頭像 發(fā)表于 02-15 10:19 ?424次閱讀

    Linux進(jìn)程間的五種通信方式介紹 5

    進(jìn)程通信IPC,InterProcess Communication)是指在不同進(jìn)程之間傳播或交換信息。IPC的方式通常有管道(包括無名
    的頭像 發(fā)表于 02-15 10:20 ?540次閱讀

    進(jìn)程通信的機(jī)制有哪些

    進(jìn)程通信(interprocess communication,簡稱IPC)指兩個進(jìn)程之間的通信。系統(tǒng)中的每一個
    的頭像 發(fā)表于 07-21 11:23 ?849次閱讀
    <b class='flag-5'>進(jìn)程</b>間<b class='flag-5'>通信</b>的機(jī)制有哪些

    RPC 和 REST 區(qū)別是什么

    01. 既 REST ,何 RPC ? 在 OpenStack 里的進(jìn)程通信方式主要有兩種,一種是基于HTTP協(xié)議的RESTFul API方式,另一種則是RPC調(diào)用。 那么這兩種方式
    的頭像 發(fā)表于 11-02 10:40 ?2881次閱讀
    <b class='flag-5'>RPC</b> 和 REST 區(qū)別是什么

    進(jìn)程通信方式總結(jié)

    進(jìn)程通信IPC): 進(jìn)程通信的方式有很多,這里主要講到進(jìn)程
    的頭像 發(fā)表于 11-09 09:25 ?657次閱讀
    <b class='flag-5'>進(jìn)程</b>間<b class='flag-5'>通信</b>方式總結(jié)

    鴻蒙開發(fā)通信與連接:ohos.rpc RPC通信

    本模塊提供進(jìn)程通信能力,包括設(shè)備內(nèi)的進(jìn)程通信IPC)和設(shè)備間的進(jìn)程
    的頭像 發(fā)表于 06-21 09:40 ?396次閱讀
    鴻蒙<b class='flag-5'>開發(fā)</b><b class='flag-5'>通信</b>與連接:ohos.<b class='flag-5'>rpc</b> <b class='flag-5'>RPC</b><b class='flag-5'>通信</b>