一、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)槊總€(gè)進(jìn)程都有自己獨(dú)立的資源和內(nèi)存空間,其他進(jìn)程不能隨意訪問不同進(jìn)程的內(nèi)存和資源,IPC/RPC便是為了突破這一點(diǎn)。IPC和RPC通常采用客戶端-服務(wù)器(Client-Server)模型,在使用時(shí),請求服務(wù)的(Client)一端進(jìn)程可獲取提供服務(wù)(Server)一端所在進(jìn)程的代理(Proxy),并通過此代理讀寫數(shù)據(jù)來實(shí)現(xiàn)進(jìn)程間的數(shù)據(jù)通信,更具體的講,首先請求服務(wù)的(Client)一端會建立一個(gè)服務(wù)提供端(Server)的代理對象,這個(gè)代理對象具備和服務(wù)提供端(Server)一樣的功能,若想訪問服務(wù)提供端(Server)中的某一個(gè)方法,只需訪問代理對象中對應(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要和某個(gè)具體的SA通信,必須先從SAMgr中獲取該SA的代理,然后使用代理和SA通信。下文直接使用Proxy表示服務(wù)請求方,Stub表示服務(wù)提供方。


約束與限制
? ● 單個(gè)設(shè)備上跨進(jìn)程通信時(shí),傳輸?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í)定義的未實(shí)現(xiàn)的方法必須在雙方繼承時(shí)給出實(shí)現(xiàn),這保證了繼承雙方不是抽象類。然后,需要編寫Stub端相關(guān)類及其接口,并且實(shí)現(xiàn)AsObject方法及OnRemoteRequest方法。同時(shí),也需要編寫Proxy端,實(shí)現(xiàn)接口類中的方法和AsObject方法,也可以封裝一些額外的方法用于調(diào)用SendRequest向?qū)Χ税l(fā)送數(shù)據(jù)。以上三者都具備后,便可以向SAMgr注冊SA了,此時(shí)的注冊應(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ǔ)庫下,請?jiān)黾訉tils的依賴:
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,指定綁定成功、綁定失敗、斷開連接時(shí)的回調(diào)函數(shù);最后,使用featureAbility提供的接口綁定Ability。
import rpc from "@ohos.rpc"
import featureAbility from "@ohos.ability.featureAbility"
let proxy = null
let connectId = null
// 單個(gè)設(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)
}
// 第一個(gè)參數(shù)是本應(yīng)用的包名,第二個(gè)參數(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期約:用于表示一個(gè)異步操作的最終完成或失敗及其結(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)
審核編輯 黃宇
-
RPC
+關(guān)注
關(guān)注
0文章
113瀏覽量
12155 -
IPC
+關(guān)注
關(guān)注
3文章
374瀏覽量
54214 -
鴻蒙
+關(guān)注
關(guān)注
60文章
2746瀏覽量
45140 -
HarmonyOS
+關(guān)注
關(guān)注
80文章
2144瀏覽量
35228
發(fā)布評論請先 登錄
鴻蒙OS跨進(jìn)程IPC與RPC通信
linux操作系統(tǒng)下的進(jìn)程通信設(shè)計(jì)
【Linux學(xué)習(xí)雜談】之進(jìn)程通信
HarmonyOS教程—基于分布式能力和IDL跨進(jìn)程通信,實(shí)現(xiàn)視頻跨設(shè)備播放、控制
【中秋國慶不斷更】HarmonyOS跨進(jìn)程通信—IPC與RPC通信開發(fā)指導(dǎo)
linux操作系統(tǒng)下的進(jìn)程通信
Linux系統(tǒng)中的進(jìn)程之間通信
Linux進(jìn)程間的五種通信方式介紹 3
Linux進(jìn)程間的五種通信方式介紹 4
Linux進(jìn)程間的五種通信方式介紹 6
Linux進(jìn)程間的五種通信方式介紹 5
進(jìn)程間通信的機(jī)制有哪些
RPC 和 REST 區(qū)別是什么

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