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