• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# OpenHarmony IDL工具规格及使用说明书(仅对系统应用开放)
2
3<!--Kit: IDL-->
4<!--Subsystem: SystemManagementServices-->
5<!--Owner: @c_bing_b-->
6<!--Designer: @liveery-->
7<!--Tester: @alien0208-->
8<!--Adviser: @huipeizi-->
9## IDL接口描述语言简介
10当客户端和服务器进行IPC通信时,需要定义双方都认可的接口,以保障双方可以成功通信,OpenHarmony IDL(OpenHarmony Interface Definition Language)则是一种定义此类接口的工具。OpenHarmony IDL先把需要传递的对象分解成操作系统能够理解的基本类型,并根据开发者的需要封装跨边界的对象。
11
12  **图1** IDL接口描述
13
14![IDL-interface-description](./figures/IDL-interface-description.png)
15
16 **OpenHarmony IDL接口描述语言主要用于:**
17
18- 声明系统服务对外提供的服务接口,根据接口声明在编译时生成跨进程调用(IPC)或跨设备调用(RPC)的代理(Proxy)和桩(Stub)的C/C++代码或JS/TS代码。
19
20- 声明Ability对外提供的服务接口,根据接口声明在编译时生成跨进程调用(IPC)或跨设备调用(RPC)的代理(Proxy)和桩(Stub)的C/C++代码或JS/TS代码。
21
22**图2** IPC/RPC通信模型
23
24![IPC-RPC-communication-model](./figures/IPC-RPC-communication-model.png)
25
26 **使用OpenHarmony IDL接口描述语言声明接口具有以下优点:**
27
28- OpenHarmony IDL中是以接口的形式定义服务,可以专注于定义而隐藏实现细节。
29
30- OpenHarmony IDL中定义的接口可以支持跨进程调用或跨设备调用。根据OpenHarmony IDL中的定义生成的信息或代码可以简化跨进程或跨设备调用接口的实现。
31
32## IDL接口描述语言构成
33
34### 数据类型
35
36#### 基础数据类型
37| IDL基本数据类型 | C++基本数据类型 | TS基本数据类型 |
38|   --------    |  --------     | --------     |
39|void           | void          | void         |
40|boolean        | bool          | boolean      |
41|byte           | int8_t        | number       |
42|short          | int16_t       | number       |
43|int            | int32_t       | number       |
44|long           | int64_t       | number       |
45|float          | float         | number       |
46|double         | double        | number       |
47|String         | std::string   | string       |
48
49IDL支持的基本数据类型及其映射到C++、TS上的数据类型的对应关系如上表所示。
50
51#### sequenceable数据类型
52sequenceable数据类型是指使用“sequenceable”关键字声明的数据,表明该数据类型可以被序列化进行跨进程或跨设备传递。sequenceable在C++与TS中声明方式存在一定差异。
53
54在C++中sequenceable数据类型的声明放在文件的头部,以“sequenceable includedir..namespace.typename”的形式声明。具体而言。声明可以有如下三个形式:
55
56```cpp
57sequenceable includedir..namespace.typename
58sequenceable includedir...typename
59sequenceable namespace.typename
60```
61
62其中,includedir表示该数据类型头文件所在目录,includedir中以“.”作为分隔符。namespace表示该数据类型所在命名空间,namespace中同样以“.”作为分隔符。typename表示数据类型,数据类型中不能包含非英文字符类型的其他符号。includedir与namespace之间通过“..”分割,如果类型声明的表达式中不包含“..”,除去最后一个typename之外的字符都会被解析为命名空间。例如:
63
64```cpp
65sequenceable a.b..C.D
66```
67
68 上述声明在生成的的C++头文件中将被解析为如下代码:
69
70```cpp
71#include  "a/b/d.h"
72using C::D;
73```
74
75TS声明放在文件的头部,以 “sequenceable namespace.typename;”的形式声明。具体而言,声明可以有如下形式(idl为对应namespace,MySequenceable为对应typename):
76
77```ts
78sequenceable idl.MySequenceable
79```
80
81其中,namespace是该类型所属的命名空间,typename是类型名。MySequenceable类型表示可以通过Parcel进行跨进程传递。sequenceable数据类型并不在OpenHarmony IDL文件中定义,而是定义在.ts文件中。因此,OpenHarmony IDL工具将根据声明在生成的.ts代码文件中加入如下语句:
82
83```ts
84import MySequenceable from "./my_sequenceable"
85```
86
87需要注意的是,IDL并不负责该类型的代码实现,仅仅按照指定的形式引入该头文件或import指定模块,并使用该类型,因此开发者需要自行保证引入目录、命名空间及类型的正确性。
88
89#### 接口类型
90 接口类型是指OpenHarmony IDL文件中定义的接口。对于当前IDL文件中定义的接口,可以直接使用它作为方法参数类型或返回值类型。而在其它OpenHarmony IDL文件中定义的接口,则需要在文件的头部进行前置声明。
91
92 C++中声明的形式与sequenceable类型相似,具体而言可以有如下形式:
93
94```cpp
95interface includedir..namespace.typename
96```
97
98 TS中声明的形式,具体而言可以有如下形式:
99
100```ts
101interface namespace.interfacename
102```
103
104其中,namespace是该接口所属的命名空间,interfacename是接口名。例如:“interface OHOS.IIdlTestObserver;”声明了在其他OpenHarmony IDL文件定义的IIdlTestObserver接口,该接口可以作为当前定义中方法的参数类型或返回值类型使用。OpenHarmony IDL工具将根据该声明在生成的TS代码文件中加入如下语句:
105
106```ts
107import IIdlTestObserver from "./i_idl_test_observer"
108```
109
110#### 数组类型
111数组类型使用“T[]”表示,其中T可以是基本数据类型、sequenceable数据类型、interface类型和数组类型。该类型在C++生成代码中将被生成为std::vector&lt;T&gt;类型。
112OpenHarmony IDL数组数据类型与TS数据类型、C++数据类型的对应关系如下表所示:
113
114|OpenHarmony IDL数据类型  | C++数据类型           | TS数据类型     |
115|   -------              |  --------            |  --------    |
116|T[]                     | std::vector&lt;T&gt; | T[]          |
117
118#### 容器类型
119IDL支持两种容器类型,即List和Map。其中List类型容器的用法为List&lt;T&gt;;Map容器的用法为Map<KT,VT>,其中T、KT、VT为基本数据类型、sequenceable类型、interface类型、数组类型或容器类型。
120
121List类型在C++代码中被映射为std::list,Map容器被映射为std::map。
122
123List类型在TS代码中不支持,Map容器被映射为Map。
124
125OpenHarmony IDL容器数据类型与Ts数据类型、C++数据类型的对应关系如下表所示:
126
127|OpenHarmony IDL数据类型  | C++数据类型       | TS数据类型     |
128|   --------             |  --------        |  -------     |
129|List&lt;T&gt;           | std::list        | 不支持        |
130|Map<KT,VT>              | std::map         | Map          |
131
132
133### IDL文件编写规范
134一个idl文件只能定义一个interface类型,且该interface名称必须和文件名相同。idl文件的接口定义使用BNF范式描述,其基本定义的形式如下:
135
136```
137[<*interface_attr_declaration*>]interface<*interface_name_with_namespace*>{<*method_declaration*>}
138```
139
140其中,<*interface_attr_declaration*>表示接口属性声明。当前仅支持“oneway”属性,表示该接口中的接口都是单向方法,即调用方法后不用等待该方法执行即可返回。这个属性为可选项,如果未声明该属性,则默认为同步调用方法。接口名需要包含完整的接口头文件目录及命名空间,且必须包含方法声明,不允许出现空接口。
141接口内的方法声明形式为:
142
143```
144[<*method_attr_declaration*>]<*result_type*><*method_declaration*>
145```
146
147其中,<*method_attr_declaration*>表示接口属性说明。当前仅支持“oneway”属性,表示该方法为单向方法,即调用方法后不用等待该方法执行即可返回。这个属性为可选项,如果未声明该属性,则默认为同步调用方法。<*result_type*>为返回值类型,<*method_declaration*>是方法名和各个参数声明。
148参数声明的形式为:
149
150```
151[<*formal_param_attr*>]<*type*><*identifier*>
152```
153
154其中<*formal_param_attr*>的值为“in”,“out”,“inout”,分别表示该参数是输入参数,输出参数或输入输出参数。需要注意的是,如果一个方法被声明为oneway,则该方法不允许有输出类型的参数(及输入输出类型)和返回值。
155
156## 开发步骤
157
158### 获取IDL工具
159#### 方法一(推荐):
1601. 在linux系统,下载OpenHarmony的两个仓:[ability_idl_tool](https://gitee.com/openharmony/ability_idl_tool)代码仓、[third_party_bounds_checking_function](https://gitee.com/openharmony/third_party_bounds_checking_function)代码仓。
1612. 进入[ability_idl_tool](https://gitee.com/openharmony/ability_idl_tool)代码仓,在Makefile所在目录执行make命令(**注意修改MakefileLinux中关于bounds_checking_function的相对位置**)。
1623. make执行完成后,在当前目录下会生成idl-gen可执行文件,可用于idl文件本地调试。
163
164#### 方法二:
165首先,打开DevEco Studio—>Tools—>SDK Manager,查看OpenHarmony SDK的本地安装路径,此处以DevEco Studio 3.0.0.993版本为例,查看方式如下图所示。
166![SDKpath](./figures/SDKpath.png)
167![SDKpath](./figures/SDKpath2.png)
168
169进入对应路径后,查看toolchains->3.x.x.x(对应版本号命名文件夹)下是否存在idl工具的可执行文件。
170
171> **注意**:
172>
173> 请保证使用最新版的SDK,版本老旧可能导致部分语句报错。
174
175若不存在,可对应版本前往[docs仓版本目录](../../release-notes)下载SDK包,以[3.2Beta3版本](../../release-notes/OpenHarmony-v3.2-beta3.md)为例,可通过镜像站点获取。
176
177关于如何替换DevEco Studio的SDK包具体操作,参考[full-SDK替换指南](../faqs/full-sdk-compile-guide.md)中的替换方法。
178
179得到idl工具的可执行文件后,根据具体场景进行后续开发步骤。
180
181### TS开发步骤
182
183#### 创建.idl文件
184
185 开发者可以使用TS编程语言构建.idl文件。
186
187 例如,此处构建一个名为IIdlTestService.idl的文件,文件内具体内容如下:
188
189```cpp
190  interface OHOS.IIdlTestService {
191      int TestIntTransaction([in] int data);
192      void TestStringTransaction([in] String data);
193      void TestMapTransaction([in] Map<int, int> data);
194      int TestArrayTransaction([in] String[] data);
195  }
196```
197
198在idl的可执行文件所在文件夹下执行命令 `idl -gen-ts -d dir -c dir/IIdlTestService.idl`。
199
200-d后的dir为目标输出目录,以输出文件夹名为IIdlTestServiceTs为例,在idl可执行文件所在目录下执行`idl -gen-ts -d IIdlTestServiceTs -c IIdlTestServiceTs/IIdlTestService.idl`,将会在执行环境的dir目录(即IIdlTestServiceTs目录)中生成接口文件、Stub文件、Proxy文件。
201
202> **注意**:生成的接口类文件名称和.idl文件名称保持一致,否则会生成代码时会出现错误。
203
204以名为`IIdlTestService.idl`的.idl文件、目标输出文件夹为IIdlTestServiceTs为例,其目录结构应类似于:
205
206```
207├── IIdlTestServiceTs  # idl代码输出文件夹
208│   ├── i_idl_test_service.ts  # 生成文件
209│   ├── idl_test_service_proxy.ts  # 生成文件
210│   ├── idl_test_service_stub.ts  # 生成文件
211│   └── IIdlTestService.idl  # 构造的.idl文件
212└── idl.exe  # idl的可执行文件
213```
214
215#### 服务端公开接口
216
217OpenHarmony IDL工具生成的Stub类是接口类的抽象实现,并且会声明.idl文件中的所有方法。
218
219```ts
220import {testIntTransactionCallback} from "./i_idl_test_service";
221import {testStringTransactionCallback} from "./i_idl_test_service";
222import {testMapTransactionCallback} from "./i_idl_test_service";
223import {testArrayTransactionCallback} from "./i_idl_test_service";
224import IIdlTestService from "./i_idl_test_service";
225import { rpc } from "@kit.IPCKit";
226
227export default class IdlTestServiceStub extends rpc.RemoteObject implements IIdlTestService {
228    constructor(des: string) {
229        super(des);
230    }
231
232    async onRemoteMessageRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence,
233        option: rpc.MessageOption): Promise<boolean> {
234        console.log("onRemoteMessageRequest called, code = " + code);
235        if (code == IdlTestServiceStub.COMMAND_TEST_INT_TRANSACTION) {
236            let _data = data.readInt();
237            this.testIntTransaction(_data, (errCode: number, returnValue: number) => {
238                reply.writeInt(errCode);
239                if (errCode == 0) {
240                    reply.writeInt(returnValue);
241                }
242            });
243            return true;
244        } else if (code == IdlTestServiceStub.COMMAND_TEST_STRING_TRANSACTION) {
245            let _data = data.readString();
246            this.testStringTransaction(_data, (errCode: number) => {
247                reply.writeInt(errCode);
248            });
249            return true;
250        } else if (code == IdlTestServiceStub.COMMAND_TEST_MAP_TRANSACTION) {
251            let _data: Map<number, number> = new Map();
252            let _dataSize = data.readInt();
253            for (let i = 0; i < _dataSize; ++i) {
254                let key = data.readInt();
255                let value = data.readInt();
256                _data.set(key, value);
257            }
258            this.testMapTransaction(_data, (errCode: number) => {
259                reply.writeInt(errCode);
260            });
261            return true;
262        } else if (code == IdlTestServiceStub.COMMAND_TEST_ARRAY_TRANSACTION) {
263            let _data = data.readStringArray();
264            this.testArrayTransaction(_data, (errCode: number, returnValue: number) => {
265                reply.writeInt(errCode);
266                if (errCode == 0) {
267                    reply.writeInt(returnValue);
268                }
269            });
270            return true;
271        } else {
272            console.log("invalid request code" + code);
273        }
274        return false;
275    }
276
277    testIntTransaction(data: number, callback: testIntTransactionCallback): void{}
278    testStringTransaction(data: string, callback: testStringTransactionCallback): void{}
279    testMapTransaction(data: Map<number, number>, callback: testMapTransactionCallback): void{}
280    testArrayTransaction(data: string[], callback: testArrayTransactionCallback): void{}
281
282    static readonly COMMAND_TEST_INT_TRANSACTION = 1;
283    static readonly COMMAND_TEST_STRING_TRANSACTION = 2;
284    static readonly COMMAND_TEST_MAP_TRANSACTION = 3;
285    static readonly COMMAND_TEST_ARRAY_TRANSACTION = 4;
286}
287```
288
289开发者需要继承.idl文件中定义的接口类并实现其中的方法。在本示例中,我们继承了IdlTestServiceStub接口类并实现了其中的testIntTransaction、testStringTransaction、testMapTransaction和testArrayTransaction方法。具体的示例代码如下:
290
291```ts
292import {testIntTransactionCallback} from "./i_idl_test_service"
293import {testStringTransactionCallback} from "./i_idl_test_service"
294import {testMapTransactionCallback} from "./i_idl_test_service";
295import {testArrayTransactionCallback} from "./i_idl_test_service";
296import IdlTestServiceStub from "./idl_test_service_stub"
297
298
299class IdlTestImp extends IdlTestServiceStub {
300
301    testIntTransaction(data: number, callback: testIntTransactionCallback): void
302    {
303        callback(0, data + 1);
304    }
305    testStringTransaction(data: string, callback: testStringTransactionCallback): void
306    {
307        callback(0);
308    }
309    testMapTransaction(data: Map<number, number>, callback: testMapTransactionCallback): void
310    {
311        callback(0);
312    }
313    testArrayTransaction(data: string[], callback: testArrayTransactionCallback): void
314    {
315        callback(0, 1);
316    }
317}
318```
319
320在服务实现接口后,需要向客户端公开该接口,以便客户端进程绑定。如果开发者的服务要公开该接口,请扩展Ability并实现onConnect()从而返回IRemoteObject,以便客户端能与服务进程交互。服务端向客户端公开IRemoteAbility接口的代码示例如下:
321
322```ts
323import { Want } from '@kit.AbilityKit';
324import { rpc } from "@kit.IPCKit";
325
326class ServiceAbility {
327  onStart() {
328    console.info('ServiceAbility onStart');
329  }
330  onStop() {
331    console.info('ServiceAbility onStop');
332  }
333  onCommand(want: Want, startId: number) {
334    console.info('ServiceAbility onCommand');
335  }
336  onConnect(want: Want) {
337    console.info('ServiceAbility onConnect');
338    try {
339      console.log('ServiceAbility want:' + typeof(want));
340      console.log('ServiceAbility want:' + JSON.stringify(want));
341      console.log('ServiceAbility want name:' + want.bundleName)
342    } catch(err) {
343      console.log('ServiceAbility error:' + err)
344    }
345    console.info('ServiceAbility onConnect end');
346    return new IdlTestImp('connect') as rpc.RemoteObject;
347  }
348  onDisconnect(want: Want) {
349    console.info('ServiceAbility onDisconnect');
350    console.info('ServiceAbility want:' + JSON.stringify(want));
351  }
352}
353
354export default new ServiceAbility()
355```
356
357#### 客户端调用IPC方法
358
359客户端调用connectServiceExtensionAbility()以连接服务时,客户端的onAbilityConnectDone中的onConnect回调会接收服务的onConnect()方法返回的IRemoteObject实例。由于客户端和服务在不同应用内,所以客户端应用的目录内必须包含.idl文件(SDK工具会自动生成Proxy代理类)的副本。客户端的onAbilityConnectDone中的onConnect回调会接收服务的onConnect()方法返回的IRemoteObject实例,使用IRemoteObject创建IdlTestServiceProxy类的实例对象testProxy,然后调用相关IPC方法。示例代码如下:
360
361```ts
362import { Want, common } from '@kit.AbilityKit';
363import IdlTestServiceProxy from './idl_test_service_proxy';
364
365function callbackTestIntTransaction(result: number, ret: number): void {
366  if (result == 0 && ret == 124) {
367    console.log('case 1 success');
368  }
369}
370
371function callbackTestStringTransaction(result: number): void {
372  if (result == 0) {
373    console.log('case 2 success');
374  }
375}
376
377function callbackTestMapTransaction(result: number): void {
378  if (result == 0) {
379    console.log('case 3 success');
380  }
381}
382
383function callbackTestArrayTransaction(result: number, ret: number): void {
384  if (result == 0 && ret == 124) {
385    console.log('case 4 success');
386  }
387}
388
389let onAbilityConnectDone: common.ConnectOptions = {
390  onConnect: (elementName, proxy) => {
391    let testProxy: IdlTestServiceProxy = new IdlTestServiceProxy(proxy);
392    let testMap: Map<number, number> = new Map();
393    testMap.set(1, 2);
394    testProxy.testIntTransaction(123, callbackTestIntTransaction);
395    testProxy.testStringTransaction('hello', callbackTestStringTransaction);
396    testProxy.testMapTransaction(testMap, callbackTestMapTransaction);
397    testProxy.testArrayTransaction(['1','2'], callbackTestArrayTransaction);
398  },
399  onDisconnect: (elementName) => {
400    console.log('onDisconnectService onDisconnect');
401  },
402  onFailed: (code) => {
403    console.log('onDisconnectService onFailed');
404  }
405};
406
407let context: common.UIAbilityContext = this.context;
408
409function connectAbility(): void {
410    let want: Want = {
411        bundleName: 'com.example.myapplicationidl',
412        abilityName: 'com.example.myapplicationidl.ServiceAbility'
413    };
414    let connectionId = -1;
415    connectionId = context.connectServiceExtensionAbility(want, onAbilityConnectDone);
416}
417
418
419```
420
421#### IPC传递sequenceable对象
422
423开发者可以通过 IPC 接口,将某个类从一个进程发送至另一个进程。但是,必须确保 IPC 通道的另一端可使用该类的代码,并且该类必须支持marshalling和unmarshalling方法。系统需要通过marshalling和unmarshalling方法将对象序列化和反序列化成各进程能识别的对象。
424
425 **如需创建支持sequenceable 类型数据,开发者必须执行以下操作:**
426
4271. 实现marshalling方法,它会获取对象的当前状态并将其序列化后写入Parcel。
4282. 实现unmarshalling方法,它会从Parcel中反序列化出对象。
429
430MySequenceable类的代码示例如下:
431
432```ts
433import { rpc } from '@kit.IPCKit';
434
435export default class MySequenceable implements rpc.Parcelable {
436  constructor(num: number, str: string) {
437    this.num = num;
438    this.str = str;
439  }
440  getNum() : number {
441    return this.num;
442  }
443  getString() : string {
444    return this.str;
445  }
446  marshalling(messageParcel: rpc.MessageSequence) {
447    messageParcel.writeInt(this.num);
448    messageParcel.writeString(this.str);
449    return true;
450  }
451  unmarshalling(messageParcel: rpc.MessageSequence) {
452    this.num = messageParcel.readInt();
453    this.str = messageParcel.readString();
454    return true;
455  }
456  private num: number;
457  private str: string;
458}
459```
460
461### C++开发SA步骤(编译期自动生成SA接口模板代码)
462
463#### 创建.idl文件
464
465开发者使用C++编程语言构建.idl文件。
466
467例如,此处构建一个名为IQuickFixManager.idl的文件,文件内具体内容如下:
468
469```
470/*
471 * Copyright (c) 2023 Huawei Device Co., Ltd.
472 * Licensed under the Apache License, Version 2.0 (the "License");
473 * you may not use this file except in compliance with the License.
474 * You may obtain a copy of the License at
475 *
476 *     http://www.apache.org/licenses/LICENSE-2.0
477 *
478 * Unless required by applicable law or agreed to in writing, software
479 * distributed under the License is distributed on an "AS IS" BASIS,
480 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
481 * See the License for the specific language governing permissions and
482 * limitations under the License.
483 */
484
485sequenceable QuickFixInfo..OHOS.AAFwk.ApplicationQuickFixInfo;
486interface OHOS.AAFwk.IQuickFixManager {
487    void ApplyQuickFix([in] String[] quickFixFiles, [in] boolean isDebug);
488    void GetApplyedQuickFixInfo([in] String bundleName, [out] ApplicationQuickFixInfo quickFixInfo);
489    void RevokeQuickFix([in] String bundleName);
490}
491```
492
493#### 修改BUILD.gn文件
494提供两种配置方法,选择其中一种即可
495
496##### 修改方法一(推荐,支持批量处理idl文件并编译为so)
497
4981. 导入IDL工具模板到当前BUILD.gn文件。
499
500   ```bash
501   # 此处不需要修改,直接复制到gn中即可
502   import("//foundation/ability/idl_tool/idl_config.gni")
503   ```
504
5052. 调用IDL工具生成C++模板文件。
506
507   ```bash
508
509   # 使用idl_gen_interface生成模板文件、输入的参数名在deps中会使用
510   idl_gen_interface("EEEFFFGGG") {
511     # 开发者定义的.idl名,须与gn文件在同一路径下
512     sources = [
513      "IAxxBxxCxx.idl",
514      "IAxxBxxCxx2.idl",
515     ]
516
517     # 根据idl文件中对自定义对象的使用,编译为so时需要增加自定义对应使用的cpp的编译,默认为空
518     sources_cpp = []
519
520     # 编译so时增加configs配置
521     configs = []
522
523     # 编译so时增加public_deps配置
524     sequenceable_pub_deps = []
525
526     # 编译so时增加external_deps配置
527     sequenceable_ext_deps = []
528
529     # 编译so时增加innerapi_tags
530     innerapi_tags = ""
531
532     # 编译so时增加sanitize
533     sanitize = ""
534
535
536     # 开启hitrace,值是hitrace_meter.h文件中定义的uint64_t类型标识,需要填入常量的变量名
537     hitrace = "HITRACE_TAG_ABILITY_MANAGER"
538
539     # 开启hilog,Domain ID 使用16进制的整数
540     log_domainid = "0xD003900"
541     # 开启hilog,字符串类型tag名、一般为子系统名称
542     log_tag = "QuickFixManagerService"
543
544     # 必填:编译so时增加subsystem_name,与业务保持一致,如quick_fix使用:
545     subsystem_name = "ability"
546     # 必填:编译so时增加part_name,与业务保持一致,如quick_fix使用:
547     part_name = "ability_runtime"
548   }
549   ```
550
551   配置hilog,参数log_domainid和log_tag必须成对出现,若只写一个会编译错误,quick_fix示例如下:
552
553   ```bash
554   idl_gen_interface("quickfix_manager_interface") {
555     sources = [
556      "IQuickFixManager.idl"
557     ]
558     hitrace = "HITRACE_TAG_ABILITY_MANAGER"
559     log_domainid = "0xD003900"
560     log_tag = "QuickFixManagerService"    #只有一个log_tag,编译会错误,同理只有log_domainid,编译也会错误
561   }
562   ```
563
5643. 在BUILD.gn中添加依赖“EEEFFFGGG”。
565
566   ```bash
567   deps = [
568      # 使用idl_gen_interface函数参数名,前面加上lib,后面加上_proxy和_stub即为生成的so名称
569      ":libEEEFFFGGG_proxy", # 如果需要 proxy 的so,加上这个依赖
570      ":libEEEFFFGGG_stub", # 如果需要 stub 的so,加上这个依赖
571     ]
572   ```
573
574   deps添加的依赖名,必须同idl_gen_interface函数参数名相同,quick_fix示例如下:
575
576   ```bash
577   idl_gen_interface("quickfix_manager_interface") {
578     sources = [
579      "IQuickFixManager.idl"
580     ]
581     hitrace = "HITRACE_TAG_ABILITY_MANAGER"
582     log_domainid = "0xD003900"
583     log_tag = "QuickFixManagerService"
584   }
585   deps = [
586    "${ability_runtime_innerkits_path}/app_manager:app_manager",
587    ":libquickfix_manager_interface_proxy", # idl_gen_interface函数参数名前面加上lib,后面加上_proxy
588    ":libquickfix_manager_interface_stub", # idl_gen_interface函数参数名前面加上lib,后面加上_stub
589   ]
590   ```
591
5926. 在BUILD.gn中添加模板文件的外部依赖。
593
594   模板文件的外部依赖需要自己添加到external_deps里。
595   若之前已存在,不需要重复添加,若重复添加会导致编译错误。
596
597   ```bash
598     external_deps = [
599     # 模板文件必须的依赖
600     "c_utils:utils",
601     # hilog输出必须的依赖
602     "hilog:libhilog",
603     # hitrace输出必须的依赖(如果idl_gen_interface中未配置hitrace,则不需要此依赖)
604     "hitrace:hitrace_meter",
605     # 模板文件必须的依赖
606     "ipc:ipc_core",
607   ]
608   ```
609
610##### 修改方法二
611
6121. 导入IDL工具模板到当前BUILD.gn文件。
613
614   ```bash
615   # 此处不需要修改,直接复制到gn中即可
616   import("//foundation/ability/idl_tool/idl_config.gni")
617   ```
618
6192. 调用IDL工具生成C++模板文件。
620
621   示例中的axx_bxx_cxx需要替换为生成的stub和proxy的.cpp名。
622
623   ```bash
624   idl_interface_sources = [
625     # axx_bxx_cxx为需要修改为生成proxy的.cpp名
626     "${target_gen_dir}/axx_bxx_cxx_proxy.cpp",
627     # axx_bxx_cxx为需要修改为生成stub的.cpp名
628     "${target_gen_dir}/axx_bxx_cxx_stub.cpp",
629   ]
630
631   # 使用idl_gen_interface生成模板文件、需输入参数名后面的deps中会使用
632   idl_gen_interface("EEEFFFGGG") {
633     # 开发者定义的.idl名,与gn文件在同一路径下
634     src_idl = rebase_path("IAxxBxxCxx.idl")
635     # proxy和stub模板.cpp文件, 此处不需要修改,直接复制到gn中即可
636     dst_file = string_join(",", idl_interface_sources)
637     # 开启hitrace,值是hitrace_meter.h文件中定义的uint64_t类型标识,需要填入常量的变量名
638     hitrace = "HITRACE_TAG_ABILITY_MANAGER"
639     # 开启hilog,Domain ID 使用16进制的整数
640     log_domainid = "0xD003900"
641     # 开启hilog,字符串类型tag名、一般为子系统名称
642     log_tag = "QuickFixManagerService"
643   }
644   ```
645
646   axx_bxx_cxx_proxy.cppaxx_bxx_cxx_stub.cpp的命名与.idl文件小写名相同,遇到大写时加"_"。
647
648   ```bash
649   # 例:.idl文件为IQuickFixManager.idl
650   axx_bxx_cxx_proxy.cpp为:quick_fix_manager_proxy.cpp
651   axx_bxx_cxx_stub.cpp为:quick_fix_manager_stub.cpp
652   ```
653
654   如果需要生成的模板文件名第一个字母为I时,需要在interface命名时在前面加一个I。
655
656   ```bash
657   # 例:生成的模板文件为quick_fix_manager_proxy.cpp时interface的名称应为IQuickFixManager
658   # .idl文件中的定义
659   interface OHOS.AAFwk.IQuickFixManager {
660       void ApplyQuickFix([in] String[] quickFixFiles, [in] boolean isDebug);
661       void GetApplyedQuickFixInfo([in] String bundleName, [out] ApplicationQuickFixInfo quickFixInfo);
662       void RevokeQuickFix([in] String bundleName);
663   }
664   ```
665
666   配置hilog,参数log_domainid和log_tag必须成对出现,若只写一个会编译错误。
667
668   ```bash
669   idl_gen_interface("quickfix_manager_interface") {
670     src_idl = rebase_path("IQuickFixManager.idl")
671     dst_file = string_join(",", idl_interface_sources)
672     hitrace = "HITRACE_TAG_ABILITY_MANAGER"
673     log_domainid = "0xD003900"
674     log_tag = "QuickFixManagerService"    #只有一个log_tag,编译会错误,同理只有log_domainid,编译也会错误
675   }
676   ```
677
6783. 在BUILD.gn中添加模板文件的头文件路径。
679
680   只需将“${target_gen_dir}”名添加到现有include_dirs中即可,其它不需要更改。
681
682   ```bash
683   include_dirs = [
684     "aaa/bbb/ccc",        # 原有头文件路径
685     "${target_gen_dir}",  # 模板头文件路径
686   ]
687   ```
688
6894. 在BUILD.gn中添加模板文件.cpp文件路径。
690
691   若sources中有axx_bxx_cxx_proxy.cppaxx_bxx_cxx_stub.cpp需要删除,并加上sources += filter_include(output_values, [ "*.cpp" ])。
692
693   ```bash
694   output_values = get_target_outputs(":EEEFFFGGG") # 返回给定目标标签的输出文件列表,替换EEEFFFGGG
695   sources = [ "axx_bxx_cxx_proxy.cpp" ]  # 需要删除axx_bxx_cxx_proxy.cpp
696   sources += filter_include(output_values, [ "*.cpp" ]) # filter_include选中符合的列表,直接复制即可
697   ```
698
6995. 在BUILD.gn中添加依赖“EEEFFFGGG”。
700
701   ```bash
702   deps = [
703       ":EEEFFFGGG",
704     ]
705   ```
706
707   deps添加的依赖名,必须同idl_gen_interface函数参数名相同。
708
709   ```bash
710   idl_gen_interface("quickfix_manager_interface") {
711     src_idl = rebase_path("IQuickFixManager.idl")
712     dst_file = string_join(",", idl_interface_sources)
713     hitrace = "HITRACE_TAG_ABILITY_MANAGER"
714     log_domainid = "0xD003900"
715     log_tag = "QuickFixManagerService"
716   }
717   deps = [
718    "${ability_runtime_innerkits_path}/app_manager:app_manager",
719    ":quickfix_manager_interface"]    # idl_gen_interface函数参数名相同
720   ```
721
7226. 在BUILD.gn中添加模板文件的外部依赖。
723
724   模板文件的外部依赖需要自己添加到external_deps里。
725
726   若之前已存在,不需要重复添加,若重复添加会导致编译错误。
727
728   ```bash
729     external_deps = [
730     # 模板文件必须的依赖
731     "c_utils:utils",
732     # hilog输出必须的依赖
733     "hilog:libhilog",
734     # hitrace输出必须的依赖
735     "hitrace:hitrace_meter",
736     # 模板文件必须的依赖
737     "ipc:ipc_core",
738   ]
739   ```
740
741#### 实例
742
743**以应用快速修复服务为例:**
744
7451. 创建名为IQuickFixManager.idl文件。
746
747   在创建.idl文件时,interface名称必须和.idl文件名相同,否则会在生成代码时出现错误。
748
749   创建.idl的文件路径与功能代码BUILD.gn的路径相同。
750
751   实例中的位置为:foundation/ability/ability_runtime/interfaces/inner_api/quick_fix/。
752
753   ```bash
754   /*
755    * Copyright (c) 2023 Huawei Device Co., Ltd.
756    * Licensed under the Apache License, Version 2.0 (the "License");
757    * you may not use this file except in compliance with the License.
758    * You may obtain a copy of the License at
759    *
760    *     http://www.apache.org/licenses/LICENSE-2.0
761    *
762    * Unless required by applicable law or agreed to in writing, software
763    * distributed under the License is distributed on an "AS IS" BASIS,
764    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
765    * See the License for the specific language governing permissions and
766    * limitations under the License.
767    */
768
769   sequenceable QuickFixInfo..OHOS.AAFwk.ApplicationQuickFixInfo;
770   interface OHOS.AAFwk.IQuickFixManager {
771       void ApplyQuickFix([in] String[] quickFixFiles, [in] boolean isDebug);
772       void GetApplyedQuickFixInfo([in] String bundleName, [out] ApplicationQuickFixInfo quickFixInfo);
773       void RevokeQuickFix([in] String bundleName);
774   }
775   ```
776
777   在创建.idl文件时,需要将返回值为int的函数,修改为void。
778
779   ```bash
780   # 例 quick_fix_manager_client.h中的函数
781       int32_t ApplyQuickFix(const std::vector<std::string> &quickFixFiles);
782       int32_t GetApplyedQuickFixInfo(const std::string &bundleName, ApplicationQuickFixInfo &quickFixInfo);
783       int32_t RevokeQuickFix(const std::string &bundleName);
784   # .idl文件中的定义
785   interface OHOS.AAFwk.QuickFixManager {
786       void ApplyQuickFix([in] String[] quickFixFiles);
787       void GetApplyedQuickFixInfo([in] String bundleName, [out] ApplicationQuickFixInfo quickFixInfo);
788       void RevokeQuickFix([in] String bundleName);
789   }
790   ```
791
7922. 修改BUILD.gn文件。
793
794   ```bash
795   # Copyright (c) 2023 Huawei Device Co., Ltd.
796   # Licensed under the Apache License, Version 2.0 (the "License");
797   # you may not use this file except in compliance with the License.
798   # You may obtain a copy of the License at
799   #
800   #     http://www.apache.org/licenses/LICENSE-2.0
801   #
802   # Unless required by applicable law or agreed to in writing, software
803   # distributed under the License is distributed on an "AS IS" BASIS,
804   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
805   # See the License for the specific language governing permissions and
806   # limitations under the License.
807
808   import("//build/ohos.gni")
809   import("//foundation/ability/ability_runtime/ability_runtime.gni")
810   import("//foundation/ability/idl_tool/idl_config.gni")
811
812   idl_interface_sources = [
813     "${target_gen_dir}/quick_fix_manager_proxy.cpp",
814     "${target_gen_dir}/quick_fix_manager_stub.cpp",
815   ]
816
817   idl_gen_interface("quickfix_manager_interface") {
818     src_idl = rebase_path("IQuickFixManager.idl")
819     dst_file = string_join(",", idl_interface_sources)
820     hitrace = "HITRACE_TAG_ABILITY_MANAGER"
821     log_domainid = "0xD003900"
822     log_tag = "QuickFixManagerService"
823   }
824
825   config("quickfix_config") {
826     visibility = [ ":*" ]
827     include_dirs = [
828       "include",
829       "${target_gen_dir}",
830     ]
831     cflags = []
832     if (target_cpu == "arm") {
833       cflags += [ "-DBINDER_IPC_32BIT" ]
834     }
835   }
836
837   ohos_shared_library("quickfix_manager") {
838     configs = [ "${ability_runtime_services_path}/common:common_config" ]
839     public_configs = [ ":quickfix_config" ]
840
841     output_values = get_target_outputs(":quickfix_manager_interface")
842     sources = [
843       "src/quick_fix_error_utils.cpp",
844       "src/quick_fix_info.cpp",
845       "src/quick_fix_load_callback.cpp",
846       "src/quick_fix_manager_client.cpp",
847       "src/quick_fix_utils.cpp",
848     ]
849     sources += filter_include(output_values, [ "*.cpp" ])
850     defines = [ "AMS_LOG_TAG = \"QuickFixService\"" ]
851     deps = [
852       ":quickfix_manager_interface",
853       "${ability_runtime_innerkits_path}/app_manager:app_manager",
854     ]
855
856     external_deps = [
857       "ability_base:want",
858       "bundle_framework:appexecfwk_base",
859       "bundle_framework:appexecfwk_core",
860       "c_utils:utils",
861       "hilog:libhilog",
862       "hitrace:hitrace_meter",
863       "ipc:ipc_single",
864       "safwk:system_ability_fwk",
865       "samgr:samgr_proxy",
866     ]
867
868     innerapi_tags = [ "platformsdk" ]
869     subsystem_name = "ability"
870     part_name = "ability_runtime"
871   }
872   ```
873
8743. 生成模板文件的路径及目录结构。
875
876   编译以rk3568为例,实例中生成的模板文件路径为:out/rk3568/gen/foundation/ability/ability_runtime/interfaces/inner_api/quick_fix/。
877
878   其中foundation/ability/ability_runtime/interfaces/inner_api/quick_fix/为.idl文件所在的相对路径。
879
880   生成文件目录结构为:
881
882   ```bash
883   |-- out/rk3568/gen/foundation/ability/ability_runtime/interfaces/inner_api/quick_fix/
884      |-- iquick_fix_manager.h
885      |-- quick_fix_manager_stub.h
886      |-- quick_fix_manager_stub.cpp
887      |-- quick_fix_manager_proxy.h
888      |-- quick_fix_manager_proxy.cpp
889   ```
890
891
892## 相关实例
893
894针对IDL的使用,有以下相关实例可供参考:
895
896- [Ability与ServiceExtensionAbility通信(ArkTS)(Full SDK)(API9)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/IDL/AbilityConnectServiceExtension)