OpenHarmony实战开发-IPC与RPC通信开发指导

654 篇文章 4 订阅
648 篇文章 6 订阅

场景介绍

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

接口说明

表1 Native侧IPC接口
在这里插入图片描述

开发步骤

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

ArkTS侧开发步骤

1.添加依赖

 import rpc from '@ohos.rpc';
 // 仅FA模型需要导入@ohos.ability.featureAbility
 // import featureAbility from '@ohos.ability.featureAbility';

Stage模型需要获取context

import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import hilog from '@ohos.hilog';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import window from '@ohos.window';

export default class MainAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onCreate');
    let context = this.context;
  }
  onDestroy() {
    hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onDestroy');
  }
  onWindowStageCreate(windowStage: window.WindowStage) {
    // Main window is created, set main page for this ability
  	hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onWindowStageCreate');
  }
  onWindowStageDestroy() {
    // Main window is destroyed, release UI related resources
  	hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onWindowStageDestroy');
  }
  onForeground() {
    // Ability has brought to foreground
    hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onForeground');
  }
  onBackground() {
    // Ability has back to background
    hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onBackground');
  }
}

2.绑定Ability

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

 // 仅FA模型需要导入@ohos.ability.featureAbility
 // import featureAbility from "@ohos.ability.featureAbility";
 import rpc from '@ohos.rpc';
 import Want from '@ohos.app.ability.Want';
 import common from '@ohos.app.ability.common';
 import hilog from '@ohos.hilog';
 import deviceManager from '@ohos.distributedDeviceManager';
 import { BusinessError } from '@ohos.base';

 let dmInstance: deviceManager.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);

 connectId = this.context.connectServiceExtensionAbility(want,connect);

 // 跨设备绑定 
 try{
   dmInstance = deviceManager.createDeviceManager("ohos.rpc.test");
 } catch(error) {
   let err: BusinessError = error as BusinessError;
   hilog.error(0x0000, 'testTag', 'createDeviceManager errCode:' + err.code + ', errMessage:' + err.message);
 }

 // 使用deviceManager获取目标设备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);
   
   // 第一个参数是本应用的包名,第二个参数是接收deviceManager的回调函数
   connectId = this.context.connectServiceExtensionAbility(want,connect);
 }

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

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

 import rpc from '@ohos.rpc';
 import Want from '@ohos.app.ability.Want';
 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;
   }
 } 

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

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

import rpc from '@ohos.rpc';
 import hilog from '@ohos.hilog';

 // 使用期约
 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);
 }

5.断开连接

IPC通信结束后,FA模型使用featureAbility的接口断开连接,Stage模型在获取context后用提供的接口断开连接。

 import rpc from '@ohos.rpc';
 import Want from '@ohos.app.ability.Want';
 import hilog from '@ohos.hilog';
 import common from '@ohos.app.ability.common';
 // 仅FA模型需要导入@ohos.ability.featureAbility
 // import featureAbility from "@ohos.ability.featureAbility";

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

如果大家还没有掌握鸿蒙,现在想要在最短的时间里吃透它,我这边特意整理了《鸿蒙语法ArkTS、TypeScript、ArkUI、教学视频》以及《鸿蒙生态应用开发白皮书V2.0PDF》《鸿蒙开发学习手册》(共计890页)鸿蒙开发资料等…希望对大家有所帮助:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

OpenHarmony APP开发教程步骤:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

鸿蒙生态应用开发白皮书V2.0PDF:https://docs.qq.com/doc/DZVVkRGRUd3pHSnFG

在这里插入图片描述

南北双向高工技能基础:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

应用开发中高级就业技术:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

全网首发-工业级 南向设备开发就业技术:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

《鸿蒙开发学习手册》:

如何快速入门:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.基本概念
2.构建第一个ArkTS应用
3.……

在这里插入图片描述

开发基础知识:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

在这里插入图片描述

基于ArkTS 开发:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

在这里插入图片描述

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值