OpenHarmony-7.IDL工具
- IDL 工具
1.openharmony IDL工具
在OpenHarmony中,当应用/系统服务的客户端和服务端进行IPC(Inter-Process Communication)跨线程通信时,需要定义双方都认可的接口,以保障双方可以成功通信,OpenHarmony IDL(Interface Definition Language)则是一种定义此类接口的工具。OpenHarmony IDL先把需要传递的对象分解成操作系统能够理解的基本类型,并根据开发者的需要封装跨边界的对象。
OpenHarmony IDL接口描述语言主要用于:
-
声明系统服务对外提供的服务接口,根据接口声明在编译时生成跨进程调用(IPC)或跨设备调用(RPC)的代理(Proxy)和桩(Stub)的C/C++代码或JS/TS代码。
-
声明Ability对外提供的服务接口,根据接口声明在编译时生成跨进程调用(IPC)或跨设备调用(RPC)的代理(Proxy)和桩(Stub)的C/C++代码或JS/TS代码。
使用OpenHarmony IDL接口描述语言声明接口具有以下优点:
-
OpenHarmony IDL中是以接口的形式定义服务,可以专注于定义而隐藏实现细节。
-
OpenHarmony IDL中定义的接口可以支持跨进程调用或跨设备调用。根据OpenHarmony IDL中的定义生成的信息或代码可以简化跨进程或跨设备调用接口的实现。
1.1.接口说明
Native侧IPC接口
1.2.开发步骤:参考IDL开发指南
1.2.1.获取IDL工具
在linux系统,下载OpenHarmony的两个仓:ability_idl_tool代码仓、third_party_bounds_checking_function代码仓。进入ability_idl_tool代码仓,在Makefile所在目录执行make命令(注意修改MakefileLinux中关于bounds_checking_function的相对位置)。make执行完成后,在当前目录下会生成idl-gen可执行文件,可用于idl文件本地调试。
代码下载:
- https://gitee.com/openharmony/ability_idl_tool.git
- https://gitee.com/openharmony/third_party_bounds_checking_function.git
执行编译:
- 将ability_idl_tool(master分支)和third_party_bounds_checking_function下载到同级目录,然后创建third_party目录,并把third_party_bounds_checking_function改为bounds_checking_function拷贝到third_party目录。
- 修改MakefileLinux:
14 #BOUNDS_CHECK_DIR := $(abspath ../../../third_party/bounds_checking_function)
15 BOUNDS_CHECK_DIR := $(abspath ../third_party/bounds_checking_function)
44 #BOUNDS_CHECK_SOURCE_DIR := $(abspath ../../../third_party/bounds_checking_function/src)
45 BOUNDS_CHECK_SOURCE_DIR := $(abspath ../third_party/bounds_checking_function/src)
- 执行make,生成idl-gen。
1.2.2.创建.idl文件
创建目录IIdlTestService,构建名为IIdlTestService.idl的文件,内容如下:
16 interface OHOS.IIdlTestService {
17 void TestIntTransaction([in] int val);
18 void TestStringTransaction([in] String val);
19 }
在idl的可执行文件所在文件夹下执行命令:
ability_idl_tool$ ./idl-gen -gen-cpp -d IIdlTestService -c IIdlTestService/IIdlTestService.idl //生成cpp文件
ability_idl_tool$ ./idl-gen -gen-ts -d IIdlTestService -c IIdlTestService/IIdlTestService.idl //生成ts文件
-d后的dir为目标输出目录,以输出文件夹名为IIdlTestService为例,在idl可执行文件所在目录下执行./idl-gen -gen-cpp -d IIdlTestService -c IIdlTestService/IIdlTestService.idl,将会在执行环境的IIdlTestService目录中生成接口文件、Stub文件、Proxy文件。
注意:生成的接口类文件名称和.idl文件名称保持一致,否则会生成代码时会出现错误。
以IIdlTestService为例,其目录结构应类似于:
ability_idl_tool/IIdlTestService$ ls
idl_test_service_proxy.cpp idl_test_service_stub.cpp iidl_test_service.h
idl_test_service_proxy.h idl_test_service_stub.h IIdlTestService.idl
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; // 定义业务函数
};
- 定义和实现服务端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);
}
}
- 定义服务端业务函数具体实现类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;
}
- 定义和实现客户端 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;
}
- 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);
- 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
2.调用原理
在IPC模式下,当系统服务调用HDI接口时,通过proxy库将函数调用转换为IPC请求,将接口调用的参数进行序列化;IPC请求通过IPC框架发送到服务端,请求将被stub库先处理,然后对接口调用的参数进行反序列化,再转换成对服务实现的函数调用,从而实现接口调用过程。
HDI调用过程:
refer to
-https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/IDL/idl-guidelines.md#31-c%E5%BC%80%E5%8F%91%E6%AD%A5%E9%AA%A4
https://gitee.com/openharmony/docs/blob/bf3b0969af6c0a9830b6761edeb96e7f37c7d449/zh-cn/application-dev/ipc/ipc-rpc-development-guideline.md