README.md
1# IDL工具
2
3## 简介
4
5在OpenHarmony中,当应用/系统服务的客户端和服务端进行IPC(Inter-Process Communication)跨线程通信时,需要定义双方都认可的接口,以保障双方可以成功通信,OpenHarmony IDL(Interface Definition Language)则是一种定义此类接口的工具。OpenHarmony IDL先把需要传递的对象分解成操作系统能够理解的基本类型,并根据开发者的需要封装跨边界的对象。
6
7 **图1** IDL接口描述
8 
9
10OpenHarmony IDL接口描述语言主要用于:
11
12- 声明系统服务对外提供的服务接口,根据接口声明在编译时生成跨进程调用(IPC)或跨设备调用(RPC)的代理(Proxy)和桩(Stub)的C/C++代码或JS/TS代码。
13
14- 声明Ability对外提供的服务接口,根据接口声明在编译时生成跨进程调用(IPC)或跨设备调用(RPC)的代理(Proxy)和桩(Stub)的C/C++代码或JS/TS代码。
15
16**图2** IPC/RPC通信模型
17
18
19
20使用OpenHarmony IDL接口描述语言声明接口具有以下优点:
21
22- OpenHarmony IDL中是以接口的形式定义服务,可以专注于定义而隐藏实现细节。
23
24- OpenHarmony IDL中定义的接口可以支持跨进程调用或跨设备调用。根据OpenHarmony IDL中的定义生成的信息或代码可以简化跨进程或跨设备调用接口的实现。
25
26**部件内子模块职责**
27
28| 子模块名称 | 职责 |
29| ---------------- | ------------------------------------------------------------|
30| 接口文件解析模块 | 解析校验接口定义文件。 |
31| stub/proxy自动生成模块 | 根据IPC/RPC规格自动生成Stub服务端和Proxy客户端代码。 |
32
33## 目录
34
35```
36foundation/ability/idl_tool
37├── ast # idl语法解析定义代码
38├── codegen # 跨进程通信模板生成模块代码
39├── metadata # matedata自定义数据解析模块代码
40├── parser # idl解析模块代码
41├── test # 测试目录
42└── util # 公共方法代码
43```
44
45## 开发步骤
46idl工具的获取和TS开发步骤可参考[开发指南](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)。
47
48### C++开发步骤
49
50#### 创建.idl文件
51
52开发者可以使用C++编程语言构建.idl文件。
53
54例如,此处构建一个名为IIdlTestService.idl的文件,文件内具体内容如下:
55
56```cpp
57 interface OHOS.IIdlTestService {
58 int TestIntTransaction([in] int data);
59 void TestStringTransaction([in] String data);
60 }
61```
62
63在idl的可执行文件所在文件夹下执行命令 `idl -gen-cpp -d dir -c dir/IIdlTestService.idl`。
64
65-d后的dir为目标输出目录,以输出文件夹名为IIdlTestServiceCpp为例,在idl可执行文件所在目录下执行`idl -gen-cpp -d IIdlTestServiceCpp -c IIdlTestServiceCpp/IIdlTestService.idl`,将会在执行环境的dir目录(即IIdlTestServiceCpp目录)中生成接口文件、Stub文件、Proxy文件。
66
67 > **注意**:生成的接口类文件名称和.idl文件名称保持一致,否则会生成代码时会出现错误。
68
69以名为`IIdlTestService.idl`的.idl文件、目标输出文件夹为IIdlTestServiceCpp为例,其目录结构应类似于:
70
71```
72├── IIdlTestServiceCpp # idl代码输出文件夹
73│ ├── iidl_test_service.h # 生成文件
74│ ├── idl_test_service_proxy.h # 生成文件
75│ ├── idl_test_service_stub.h # 生成文件
76│ ├── idl_test_service_proxy.cpp # 生成文件
77│ ├── idl_test_service_stub.cpp # 生成文件
78│ └── IIdlTestService.idl # 构造的.idl文件
79└── idl.exe # idl的可执行文件
80```
81
82#### 服务端公开接口
83
84OpenHarmony IDL工具生成的Stub类是接口类的抽象实现,并且会声明.idl文件中的所有方法。
85
86```cpp
87#ifndef OHOS_IDLTESTSERVICESTUB_H
88#define OHOS_IDLTESTSERVICESTUB_H
89#include <iremote_stub.h>
90#include "iidl_test_service.h"
91
92namespace OHOS {
93class IdlTestServiceStub : public IRemoteStub<IIdlTestService> {
94public:
95 int OnRemoteRequest(
96 /* [in] */ uint32_t code,
97 /* [in] */ MessageParcel& data,
98 /* [out] */ MessageParcel& reply,
99 /* [in] */ MessageOption& option) override;
100
101private:
102 static constexpr int COMMAND_TEST_INT_TRANSACTION = MIN_TRANSACTION_ID + 0;
103 static constexpr int COMMAND_TEST_STRING_TRANSACTION = MIN_TRANSACTION_ID + 1;
104};
105} // namespace OHOS
106#endif // OHOS_IDLTESTSERVICESTUB_H
107```
108
109开发者需要继承.idl文件中定义的接口类并实现其中的方法,同时在服务侧初始化时需要将定义的服务注册至SAMGR中,在本示例中,TestService类继承了IdlTestServiceStub接口类并实现了其中的TestIntTransaction和TestStringTransaction方法。具体的示例代码如下:
110
111```cpp
112#ifndef OHOS_IPC_TEST_SERVICE_H
113#define OHOS_IPC_TEST_SERVICE_H
114
115#include "hilog/log.h"
116#include "log_tags.h"
117#include "idl_test_service_stub.h"
118
119namespace OHOS {
120class TestService : public IdlTestServiceStub {
121public:
122 TestService();
123 ~TestService();
124 static int Instantiate();
125 ErrCode TestIntTransaction(int data, int &rep) override;
126 ErrCode TestStringTransaction(const std::string& data) override;
127private:
128 static constexpr HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_ID_IPC, "TestService" };
129};
130} // namespace OHOS
131#endif // OHOS_IPC_TEST_SERVICE_H
132```
133
134注册服务的示例代码如下:
135
136```cpp
137#include "test_service.h"
138
139#include <string_ex.h>
140
141#include "if_system_ability_manager.h"
142#include "ipc_debug.h"
143#include "ipc_skeleton.h"
144#include "iservice_registry.h"
145#include "system_ability_definition.h"
146
147namespace OHOS {
148using namespace OHOS::HiviewDFX;
149
150int TestService::Instantiate()
151{
152 ZLOGI(LABEL, "%{public}s call in", __func__);
153 auto saMgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
154 if (saMgr == nullptr) {
155 ZLOGE(LABEL, "%{public}s:fail to get Registry", __func__);
156 return -ENODEV;
157 }
158
159 sptr<IRemoteObject> newInstance = new TestService();
160 int result = saMgr->AddSystemAbility(IPC_TEST_SERVICE, newInstance);
161 ZLOGI(LABEL, "%{public}s: IPC_TEST_SERVICE result = %{public}d", __func__, result);
162 return result;
163}
164
165TestService::TestService()
166{
167}
168
169TestService::~TestService()
170{
171}
172
173ErrCode TestService::TestIntTransaction(int data, int &rep)
174{
175 ZLOGE(LABEL, " TestService:read from client data = %{public}d", data);
176 rep = data + data;
177 return ERR_NONE;
178}
179
180ErrCode TestService::TestStringTransaction(const std::string &data)
181{
182 ZLOGE(LABEL, "TestService:read string from client data = %{public}s", data.c_str());
183 return data.size();
184}
185} // namespace OHOS
186```
187
188#### 客户端调用IPC方法
189
190C++客户端通常通过SAMGR获取系统中定义的服务代理,随后即可正常调用proxy提供的接口。示例代码如下:
191
192```cpp
193#include "test_client.h"
194
195#include "if_system_ability_manager.h"
196#include "ipc_debug.h"
197#include "ipc_skeleton.h"
198#include "iservice_registry.h"
199#include "system_ability_definition.h"
200
201namespace OHOS {
202int TestClient::ConnectService()
203{
204 auto saMgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
205 if (saMgr == nullptr) {
206 ZLOGE(LABEL, "get registry fail");
207 return -1;
208 }
209
210 sptr<IRemoteObject> object = saMgr->GetSystemAbility(IPC_TEST_SERVICE);
211 if (object != nullptr) {
212 ZLOGE(LABEL, "Got test Service object");
213 testService_ = (new (std::nothrow) IdlTestServiceProxy(object));
214 }
215
216 if (testService_ == nullptr) {
217 ZLOGE(LABEL, "Could not find Test Service!");
218 return -1;
219 }
220
221 return 0;
222}
223
224void TestClient::StartIntTransaction()
225{
226 if (testService_ != nullptr) {
227 ZLOGE(LABEL, "StartIntTransaction");
228 [[maybe_unused]] int result = 0;
229 testService_->TestIntTransaction(1234, result); // 1234 : test number
230 ZLOGE(LABEL, "Rec result from server %{public}d.", result);
231 }
232}
233
234void TestClient::StartStringTransaction()
235{
236 if (testService_ != nullptr) {
237 ZLOGI(LABEL, "StartIntTransaction");
238 testService_->TestStringTransaction("IDL Test");
239 }
240}
241} // namespace OHOS
242```
243
244## C++与TS互通开发步骤
245
246### TS Proxy与C++ Stub开发步骤
247
248#### C++端提供服务对象
249
2501. 如上所述C++开发步骤,开发者使用C++编程语言构建.idl文件,通过命令生成接口、Stub文件、Proxy文件。
251
2522. 开发者创建服务对象,并继承C++ Stub文件中定义的接口类并实现其中的方法,例如:
253
254 ```cpp
255 class IdlTestServiceImpl : public IdlTestServiceStub {
256 public:
257 IdlTestServiceImpl() = default;
258 virtual ~IdlTestServiceImpl() = default;
259
260 ErrCode TestIntTransaction(int _data, int& result) override
261 {
262 result = 256;
263 return ERR_OK;
264 }
265
266 ErrCode TestStringTransaction(const std::string& _data) override
267 {
268 return ERR_OK;
269 }
270 };
271 ```
272
273#### C++端提供napi接口
274
275C++需要通过napi的方式,把C++服务对象提供给TS端,例如:C++端提供一个GetNativeObject方法,方法里创建IdlTestServiceImpl实例,通过NAPI_ohos_rpc_CreateJsRemoteObject方法,创建出一个JS远程对象供TS应用使用,如下:
276
277```cpp
278NativeValue* GetNativeObject(NativeEngine& engine, NativeCallbackInfo& info)
279{
280 sptr<IdlTestServiceImpl> impl = new IdlTestServiceImpl();
281 napi_value napiRemoteObject = NAPI_ohos_rpc_CreateJsRemoteObject(reinterpret_cast<napi_env>(&engine), impl);
282 NativeValue* nativeRemoteObject = reinterpret_cast<NativeValue*>(napiRemoteObject);
283 return nativeRemoteObject;
284}
285```
286
287#### TS端提供Proxy对象
288
289如上所述TS开发步骤,开发者使用TS编程语言构建.idl文件,通过命令生成接口、Stub文件、Proxy文件。Proxy文件例如:
290
291```ts
292import {testIntTransactionCallback} from "./i_idl_test_service";
293import {testStringTransactionCallback} from "./i_idl_test_service";
294import IIdlTestService from "./i_idl_test_service";
295import rpc from "@ohos.rpc";
296
297export default class IdlTestServiceProxy implements IIdlTestService {
298 constructor(proxy) {
299 this.proxy = proxy;
300 }
301
302 testIntTransaction(data: number, callback: testIntTransactionCallback): void
303 {
304 let _option = new rpc.MessageOption();
305 let _data = new rpc.MessageParcel();
306 let _reply = new rpc.MessageParcel();
307 _data.writeInt(data);
308 this.proxy.sendMessageRequest(IdlTestServiceProxy.COMMAND_TEST_INT_TRANSACTION, _data, _reply, _option).then(function(result) {
309 if (result.errCode == 0) {
310 let _errCode = result.reply.readInt();
311 if (_errCode != 0) {
312 let _returnValue = undefined;
313 callback(_errCode, _returnValue);
314 return;
315 }
316 let _returnValue = result.reply.readInt();
317 callback(_errCode, _returnValue);
318 } else {
319 console.log('sendMessageRequest failed, errCode: ' + result.errCode);
320 }
321 })
322 }
323
324 testStringTransaction(data: string, callback: testStringTransactionCallback): void
325 {
326 let _option = new rpc.MessageOption();
327 let _data = new rpc.MessageParcel();
328 let _reply = new rpc.MessageParcel();
329 _data.writeString(data);
330 this.proxy.sendMessageRequest(IdlTestServiceProxy.COMMAND_TEST_STRING_TRANSACTION, _data, _reply, _option).then(function(result) {
331 if (result.errCode == 0) {
332 let _errCode = result.reply.readInt();
333 callback(_errCode);
334 } else {
335 console.log('sendMessageRequest failed, errCode: ' + result.errCode);
336 }
337 })
338 }
339
340 static readonly COMMAND_TEST_INT_TRANSACTION = 1;
341 static readonly COMMAND_TEST_STRING_TRANSACTION = 2;
342 private proxy
343}
344```
345
346#### TS与C++实现互通
347
3481. TS应用调用napi接口获取C++服务的远程对象
3492. 构建TS Proxy对象,并把C++服务的远程对象传递给它
3503. 此时开发者通过TS Proxy对象调用.idl声明的方法,实现TS Proxy与C++ Stub的互通,示例如下:
351
352```ts
353import IdlTestServiceProxy from './idl_test_service_proxy'
354import nativeMgr from 'nativeManager';
355
356function testIntTransactionCallback(errCode: number, returnValue: number)
357{
358 console.log('errCode: ' + errCode + ' returnValue: ' + returnValue);
359}
360
361function testStringTransactionCallback(errCode: number)
362{
363 console.log('errCode: ' + errCode);
364}
365
366function jsProxyTriggerCppStub()
367{
368 let nativeObj = nativeMgr.GetNativeObject();
369 let tsProxy = new IdlTestServiceProxy(nativeObj);
370 // invoke testIntTransaction
371 tsProxy.testIntTransaction(10, testIntTransactionCallback);
372
373 // invoke testStringTransaction
374 tsProxy.testStringTransaction('test', testIntTransactionCallback);
375}
376```
377
378## 相关仓
379元能力子系统
380
381[ability_base](https://gitee.com/openharmony/ability_ability_base)
382
383[ability_runtime](https://gitee.com/openharmony/ability_ability_runtime)
384
385[dmsfwk](https://gitee.com/openharmony/ability_dmsfwk)
386
387[form_fwk](https://gitee.com/openharmony/ability_form_fwk)
388
389[**idl_tool**](https://gitee.com/openharmony/ability_idl_tool)
390