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 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 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<T>类型。 112OpenHarmony IDL数组数据类型与TS数据类型、C++数据类型的对应关系如下表所示: 113 114|OpenHarmony IDL数据类型 | C++数据类型 | TS数据类型 | 115| ------- | -------- | -------- | 116|T[] | std::vector<T> | T[] | 117 118#### 容器类型 119IDL支持两种容器类型,即List和Map。其中List类型容器的用法为List<T>;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<T> | 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 167 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.cpp和axx_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.cpp和axx_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)