1# Multi-device Collaboration 2 3 4## When to Use 5 6Multi-device collaboration involves the following scenarios: 7 8- [Starting UIAbility or ServiceExtensionAbility Across Devices (No Data Returned)](#starting-uiability-or-serviceextensionability-across-devices-no-data-returned) 9 10- [Starting UIAbility Across Devices (Data Returned)](#starting-uiability-across-devices-data-returned) 11 12- [Connecting to ServiceExtensionAbility Across Devices](#connecting-to-serviceextensionability-across-devices) 13 14- [Using Cross-Device Call](#using-cross-device-call) 15 16 17## Multi-Device Collaboration Process 18 19The figure below shows the multi-device collaboration process. 20 21**Figure 1** Multi-device collaboration process 22 23 24 25 26## Constraints 27 28- Since multi-device collaboration mission management is not available, you can obtain the device list by developing system applications. Third-party applications cannot access the device list. 29 30- Multi-device collaboration must comply with [Inter-Device Component Startup Rules](component-startup-rules.md#inter-device-component-startup-rules). 31 32- For better user experience, you are advised to use the **want** parameter to transmit data smaller than 100 KB. 33 34 35## Starting UIAbility or ServiceExtensionAbility Across Devices (No Data Returned) 36 37On device A, touch the **Start** button provided by the initiator application to start a specified UIAbility or ServiceExtensionAbility on device B. 38 39 40### Available APIs 41 42**Table 1** Cross-device startup APIs 43 44| **API**| **Description**| 45| -------- | -------- | 46| startAbility(want: Want, callback: AsyncCallback<void>): void; | Starts a UIAbility or ServiceExtensionAbility. This API uses an asynchronous callback to return the result.| 47| stopServiceExtensionAbility(want: Want, callback: AsyncCallback<void>): void; | Stops a ServiceExtensionAbility. This API uses an asynchronous callback to return the result.| 48| stopServiceExtensionAbility(want: Want): Promise<void>; | Stops a ServiceExtensionAbility. This API uses a promise to return the result.| 49 50 51### How to Develop 52 531. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). 54 552. Display a dialog box to ask for authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization). 56 573. Obtain the device ID of the target device. 58 59 ```ts 60 import deviceManager from '@ohos.distributedDeviceManager'; 61 62 let dmClass: deviceManager.DeviceManager; 63 function initDmClass() { 64 // createDeviceManager is a system API. 65 try{ 66 dmClass = deviceManager.createDeviceManager('ohos.samples.demo'); 67 } catch(err) { 68 console.error("createDeviceManager err: " + JSON.stringify(err)); 69 } 70 } 71 function getRemoteDeviceId(): string | undefined { 72 if (typeof dmClass === 'object' && dmClass !== null) { 73 let list = dmClass.getAvailableDeviceListSync(); 74 if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { 75 console.info('getRemoteDeviceId err: list is null'); 76 return; 77 } 78 if (list.length === 0) { 79 console.info("getRemoteDeviceId err: list is empty"); 80 return; 81 } 82 return list[0].networkId; 83 } else { 84 console.info('getRemoteDeviceId err: dmClass is null'); 85 return; 86 } 87 } 88 ``` 89 904. Set the target component parameters, and call [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) to start a UIAbility or ServiceExtensionAbility. 91 92 ```ts 93 import { BusinessError } from '@ohos.base'; 94 import Want from '@ohos.app.ability.Want'; 95 let want: Want = { 96 deviceId: getRemoteDeviceId(), 97 bundleName: 'com.example.myapplication', 98 abilityName: 'EntryAbility', 99 moduleName: 'entry', // moduleName is optional. 100 } 101 // context is the AbilityContext of the initiator UIAbility. 102 this.context.startAbility(want).then(() => { 103 // ... 104 }).catch((err: BusinessError) => { 105 // ... 106 console.error("startAbility err: " + JSON.stringify(err)); 107 }) 108 ``` 109 1105. Call [stopServiceExtensionAbility](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstopserviceextensionability) to stop the ServiceExtensionAbility when it is no longer required on device B. (This API cannot be used to stop a UIAbility. Users must manually stop a UIAbility through mission management.) 111 112 ```ts 113 import Want from '@ohos.app.ability.Want'; 114 import { BusinessError } from '@ohos.base'; 115 let want: Want = { 116 deviceId: getRemoteDeviceId(), 117 bundleName: 'com.example.myapplication', 118 abilityName: 'FuncAbility', 119 moduleName: 'module1', // moduleName is optional. 120 } 121 // Stop the ServiceExtensionAbility started by calling startAbility(). 122 this.context.stopServiceExtensionAbility(want).then(() => { 123 console.info("stop service extension ability success") 124 }).catch((err: BusinessError) => { 125 console.info("stop service extension ability err is " + JSON.stringify(err)) 126 }) 127 ``` 128 129## Starting UIAbility Across Devices (Data Returned) 130 131On device A, touch the **Start** button provided by the initiator application to start a specified UIAbility on device B. When the UIAbility on device B exits, a value is returned to the initiator application. 132 133 134### Available APIs 135 136**Table 2** APIs for starting a UIAbility across devices and returning the result data 137 138| API| Description| 139| -------- | -------- | 140| startAbilityForResult(want: Want, callback: AsyncCallback<AbilityResult>): void; | Starts a UIAbility. This API uses an asynchronous callback to return the result when the UIAbility is terminated.| 141| terminateSelfWithResult(parameter: AbilityResult, callback: AsyncCallback<void>): void;| Terminates this UIAbility. This API uses an asynchronous callback to return the result information. It is used together with **startAbilityForResult**.| 142| terminateSelfWithResult(parameter: AbilityResult): Promise<void>; | Terminates this UIAbility. This API uses a promise to return the result information. It is used together with **startAbilityForResult**.| 143 144 145### How to Develop 146 1471. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). 148 1492. Display a dialog box to ask for authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization). 150 1513. Set the target component parameters on the initiator, and call **startAbilityForResult()** to start the target UIAbility. **data** in the asynchronous callback is used to receive the information returned by the target UIAbility to the initiator UIAbility after the target UIAbility terminates itself. For details about how to implement **getRemoteDeviceId()**, see [Starting UIAbility or ServiceExtensionAbility Across Devices (No Data Returned)](#starting-uiability-or-serviceextensionability-across-devices-no-data-returned). 152 153 ```ts 154 import AbilityConstant from '@ohos.app.ability.AbilityConstant'; 155 import common from '@ohos.app.ability.common'; 156 import { BusinessError } from '@ohos.base'; 157 import Want from '@ohos.app.ability.Want'; 158 @Entry 159 @Component 160 struct PageName { 161 private context = getContext(this) as common.UIAbilityContext; 162 build() { 163 // ... 164 Button('StartAbilityForResult') 165 .onClick(()=>{ 166 let want: Want = { 167 deviceId: getRemoteDeviceId(), 168 bundleName: 'com.example.myapplication', 169 abilityName: 'FuncAbility', 170 moduleName: 'module1', // moduleName is optional. 171 } 172 // context is the AbilityContext of the initiator UIAbility. 173 this.context.startAbilityForResult(want).then((data) => { 174 // ... 175 }).catch((error: BusinessError) => { 176 console.info("startAbilityForResult err: " + JSON.stringify(error)); 177 }) 178 } 179 ) 180 } 181 } 182 ``` 183 1844. After the UIAbility mission on the target device is complete, call **terminateSelfWithResult()** to return the data to the initiator UIAbility. 185 186 ```ts 187 import { BusinessError } from '@ohos.base'; 188 import common from '@ohos.app.ability.common'; 189 @Entry 190 @Component 191 struct PageName { 192 private context = getContext(this) as common.UIAbilityContext; 193 build() { 194 // ... 195 Button('terminateSelfWithResult') 196 .onClick(()=>{ 197 const RESULT_CODE: number = 1001; 198 // context is the AbilityContext of the target UIAbility. 199 this.context.terminateSelfWithResult( 200 { 201 resultCode: RESULT_CODE, 202 want: { 203 bundleName: 'com.example.myapplication', 204 abilityName: 'FuncAbility', 205 moduleName: 'module1', 206 }, 207 }, 208 (err: BusinessError) => { 209 // ... 210 console.info("terminateSelfWithResult err: " + JSON.stringify(err)); 211 }); 212 } 213 // ... 214 ) 215 } 216 } 217 ``` 218 2195. The initiator UIAbility receives the information returned by the target UIAbility and processes the information. 220 221 ```ts 222 import common from '@ohos.app.ability.common'; 223 import { BusinessError } from '@ohos.base'; 224 import Want from '@ohos.app.ability.Want'; 225 @Entry 226 @Component 227 struct PageName { 228 private context = getContext(this) as common.UIAbilityContext; 229 build() { 230 // ... 231 Button('StartAbilityForResult') 232 .onClick(()=>{ 233 let want: Want = { 234 deviceId: getRemoteDeviceId(), 235 bundleName: 'com.example.myapplication', 236 abilityName: 'FuncAbility', 237 moduleName: 'module1', // moduleName is optional. 238 } 239 const RESULT_CODE: number = 1001; 240 // ... 241 // context is the UIAbilityContext of the initiator UIAbility. 242 this.context.startAbilityForResult(want).then((data) => { 243 if (data?.resultCode === RESULT_CODE) { 244 // Parse the information returned by the target UIAbility. 245 let info = data.want?.parameters?.info; 246 // ... 247 } 248 }).catch((error: BusinessError) => { 249 // ... 250 }) 251 } 252 ) 253 } 254 } 255 ``` 256 257 258## Connecting to ServiceExtensionAbility Across Devices 259 260A system application can connect to a service on another device by calling [connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextconnectserviceextensionability). For example, in the distributed game scenario, a tablet is used as the remote control and a smart TV is used as the display. 261 262 263### Available APIs 264 265**Table 3** APIs for cross-device connection 266 267| API| Description| 268| -------- | -------- | 269| connectServiceExtensionAbility(want: Want, options: ConnectOptions): number; | Connects to a ServiceExtensionAbility.| 270| disconnectServiceExtensionAbility(connection: number, callback: AsyncCallback<void>): void; | Disconnects a connection. This API uses an asynchronous callback to return the result.| 271| disconnectServiceExtensionAbility(connection: number): Promise<void>; | Disconnects a connection. This API uses a promise to return the result.| 272 273 274### How to Develop 275 2761. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). 277 2782. Display a dialog box to ask for authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization). 279 2803. (Optional) [Implement a background service](serviceextensionability.md#implementing-a-background-service-for-system-applications-only). Perform this operation only if no background service is available. 281 2824. Connect to the background service. 283 - Implement the **IAbilityConnection** class. **IAbilityConnection** provides the following callbacks that you should implement: **onConnect()**, **onDisconnect()**, and **onFailed()**. The **onConnect()** callback is invoked when a service is connected, **onDisconnect()** is invoked when a service is unexpectedly disconnected, and **onFailed()** is invoked when the connection to a service fails. 284 - Set the target component parameters, including the target device ID, bundle name, and ability name. 285 - Call **connectServiceExtensionAbility** to initiate a connection. 286 - Receive the service handle returned by the target device when the connection is successful. 287 - Perform cross-device call and obtain the result returned by the target service. 288 289 ```ts 290 import rpc from '@ohos.rpc'; 291 import Want from '@ohos.app.ability.Want'; 292 import common from '@ohos.app.ability.common'; 293 import { BusinessError } from '@ohos.base'; 294 @Entry 295 @Component 296 struct PageName { 297 private context = getContext(this) as common.UIAbilityContext; 298 build() { 299 // ... 300 Button('connectServiceExtensionAbility') 301 .onClick(()=>{ 302 const REQUEST_CODE = 99; 303 let want: Want = { 304 "deviceId": getRemoteDeviceId(), 305 "bundleName": "com.example.myapplication", 306 "abilityName": "ServiceExtAbility" 307 }; 308 // The ID returned after the connection is set up must be saved. The ID will be passed for service disconnection. 309 let connectionId = this.context.connectServiceExtensionAbility(want, 310 { 311 onConnect(elementName, remote) { 312 console.info('onConnect callback'); 313 if (remote === null) { 314 console.info(`onConnect remote is null`); 315 return; 316 } 317 let option = new rpc.MessageOption(); 318 let data = new rpc.MessageSequence(); 319 let reply = new rpc.MessageSequence(); 320 data.writeInt(1); 321 data.writeInt(99); // You can send data to the target application for corresponding operations. 322 // @param code Indicates the service request code sent by the client. 323 // @param data Indicates the {@link MessageSequence} object sent by the client. 324 // @param reply Indicates the response message object sent by the remote service. 325 // @param options Specifies whether the operation is synchronous or asynchronous. 326 // 327 // @return Returns {@code true} if the operation is successful; returns {@code false} otherwise. 328 remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret: rpc.RequestResult) => { 329 let msg = reply.readInt(); // Receive the information (100) returned by the target device if the connection is successful. 330 console.info(`sendRequest ret:${ret} msg:${msg}`); 331 }).catch((error: BusinessError) => { 332 console.info('sendRequest failed'); 333 }); 334 }, 335 onDisconnect(elementName) { 336 console.info('onDisconnect callback'); 337 }, 338 onFailed(code) { 339 console.info('onFailed callback'); 340 } 341 }); 342 }) 343 } 344 } 345 ``` 346 347 For details about how to implement **getRemoteDeviceId()**, see [Starting UIAbility or ServiceExtensionAbility Across Devices (No Data Returned)](#starting-uiability-or-serviceextensionability-across-devices-no-data-returned). 348 3495. Disconnect the connection. Use **disconnectServiceExtensionAbility()** to disconnect from the background service. 350 351 ```ts 352 import common from '@ohos.app.ability.common'; 353 import { BusinessError } from '@ohos.base'; 354 @Entry 355 @Component 356 struct PageName { 357 private context = getContext(this) as common.UIAbilityContext; 358 build() { 359 // ... 360 Button('disconnectServiceExtensionAbility') 361 .onClick(()=>{ 362 let connectionId: number = 1 // ID returned when the service is connected through connectServiceExtensionAbility. 363 this.context.disconnectServiceExtensionAbility(connectionId).then(() => { 364 console.info('disconnectServiceExtensionAbility success'); 365 }).catch((error: BusinessError) => { 366 console.error('disconnectServiceExtensionAbility failed'); 367 }) 368 }) 369 } 370 } 371 ``` 372 373 374## Using Cross-Device Call 375 376The basic principle of cross-device call is the same as that of intra-device call. For details, see [Using Call to Implement UIAbility Interaction (for System Applications Only)](uiability-intra-device-interaction.md#using-call-to-implement-uiability-interaction-for-system-applications-only). 377 378The following describes how to implement multi-device collaboration through cross-device call. 379 380 381### Available APIs 382 383**Table 4** Call APIs 384 385| API| Description| 386| -------- | -------- | 387| startAbilityByCall(want: Want): Promise<Caller>; | Starts a UIAbility in the foreground or background and obtains the caller object for communicating with the UIAbility.| 388| on(method: string, callback: CalleeCallBack): void | Callback invoked when the CalleeAbility registers a method.| 389| off(method: string): void | Callback invoked when the CalleeAbility deregisters a method.| 390| call(method: string, data: rpc.Parcelable): Promise<void> | Sends agreed parcelable data to the CalleeAbility.| 391| callWithResult(method: string, data: rpc.Parcelable): Promise<rpc.MessageSequence>| Sends agreed parcelable data to the CalleeAbility and obtains the agreed parcelable data returned by the CalleeAbility.| 392| release(): void | Releases the caller object.| 393| on(type: "release", callback: OnReleaseCallback): void | Callback invoked when the caller object is released.| 394 395 396### How to Develop 397 3981. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). 399 4002. Display a dialog box to ask for authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization). 401 4023. Create the CalleeAbility. 403 404 For the CalleeAbility, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use **on()** to register a listener. When data does not need to be received, use **off()** to deregister the listener. 405 406 1. Configure the launch type of the UIAbility. 407 408 Set **launchType** of the CalleeAbility to **singleton** in the **module.json5** file. 409 410 | JSON Field| Description| 411 | -------- | -------- | 412 | "launchType"| UIAbility launch type. Set this parameter to **singleton**.| 413 414 An example of the UIAbility configuration is as follows: 415 416 417 ```json 418 "abilities":[{ 419 "name": ".CalleeAbility", 420 "srcEntry": "./ets/CalleeAbility/CalleeAbility.ts", 421 "launchType": "singleton", 422 "description": "$string:CalleeAbility_desc", 423 "icon": "$media:icon", 424 "label": "$string:CalleeAbility_label", 425 "exported": true 426 }] 427 ``` 428 2. Import the **UIAbility** module. 429 430 ```ts 431 import UIAbility from '@ohos.app.ability.UIAbility'; 432 ``` 433 3. Define the agreed parcelable data. 434 435 The data formats sent and received by the CallerAbility and CalleeAbility must be consistent. In the following example, the data formats are number and string. 436 437 438 ```ts 439 import rpc from '@ohos.rpc' 440 class MyParcelable { 441 num: number = 0; 442 str: string = ""; 443 444 constructor(num: number, string: string) { 445 this.num = num; 446 this.str = string; 447 } 448 449 marshalling(messageSequence: rpc.MessageSequence) { 450 messageSequence.writeInt(this.num); 451 messageSequence.writeString(this.str); 452 return true; 453 } 454 455 unmarshalling(messageSequence: rpc.MessageSequence) { 456 this.num = messageSequence.readInt(); 457 this.str = messageSequence.readString(); 458 return true; 459 } 460 } 461 ``` 462 4. Implement **Callee.on** and **Callee.off**. 463 464 In the following example, the **MSG_SEND_METHOD** listener is registered in **onCreate()** of the UIAbility and deregistered in **onDestroy()**. After receiving parcelable data, the application processes the data and returns the data result. You need to implement processing based on service requirements. 465 466 ```ts 467 import rpc from '@ohos.rpc'; 468 import Want from '@ohos.app.ability.Want'; 469 import UIAbility from '@ohos.app.ability.UIAbility'; 470 import AbilityConstant from '@ohos.app.ability.AbilityConstant'; 471 const TAG: string = '[CalleeAbility]'; 472 const MSG_SEND_METHOD: string = 'CallSendMsg'; 473 474 function sendMsgCallback(data: rpc.MessageSequence): MyParcelable { 475 console.info('CalleeSortFunc called'); 476 477 // Obtain the parcelable data sent by the CallerAbility. 478 let receivedData: MyParcelable = new MyParcelable(0, ''); 479 data.readParcelable(receivedData); 480 console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`); 481 482 // Process the data. 483 // Return the parcelable data result to the CallerAbility. 484 return new MyParcelable(Number(receivedData.num) + 1, `send ${receivedData.str} succeed`); 485 } 486 487 export default class CalleeAbility extends UIAbility { 488 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 489 try { 490 this.callee.on(MSG_SEND_METHOD, sendMsgCallback); 491 } catch (error) { 492 console.info(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`); 493 } 494 } 495 496 onDestroy() { 497 try { 498 this.callee.off(MSG_SEND_METHOD); 499 } catch (error) { 500 console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`); 501 } 502 } 503 } 504 ``` 505 5064. Obtain the caller object and access the CalleeAbility. 507 1. Import the **UIAbility** module. 508 509 ```ts 510 import UIAbility from '@ohos.app.ability.UIAbility'; 511 ``` 512 2. Obtain the caller object. 513 514 The **context** attribute of the UIAbility implements **startAbilityByCall** to obtain the caller object for communication. The following example uses **this.context** to obtain the **context** attribute of the UIAbility, uses **startAbilityByCall** to start the CalleeAbility, obtain the caller object, and register the **onRelease** and **onRemoteStateChange** listeners of the CallerAbility. You need to implement processing based on service requirements. 515 516 517 ```ts 518 import UIAbility, { Caller } from '@ohos.app.ability.UIAbility'; 519 import { BusinessError } from '@ohos.base'; 520 export default class EntryAbility extends UIAbility { 521 // ... 522 async onButtonGetRemoteCaller() { 523 let caller: Caller | undefined; 524 let context = this.context; 525 526 context.startAbilityByCall({ 527 deviceId: getRemoteDeviceId(), 528 bundleName: 'com.samples.CallApplication', 529 abilityName: 'CalleeAbility' 530 }).then((data) => { 531 if (data != null) { 532 caller = data; 533 console.info('get remote caller success'); 534 // Register the onRelease listener of the CallerAbility. 535 caller.onRelease((msg) => { 536 console.info(`remote caller onRelease is called ${msg}`); 537 }) 538 console.info('remote caller register OnRelease succeed'); 539 // Register the onRemoteStateChange listener of the CallerAbility. 540 try { 541 caller.onRemoteStateChange((str) => { 542 console.info('Remote state changed ' + str); 543 }); 544 } catch (error) { 545 console.info('Caller.onRemoteStateChange catch error, error.code: ${JSON.stringify(error.code)}, error.message: ${JSON.stringify(error.message)}'); 546 } 547 } 548 }).catch((error: BusinessError) => { 549 console.error(`get remote caller failed with ${error}`); 550 }) 551 } 552 // ... 553 } 554 ``` 555 556 For details about how to implement **getRemoteDeviceId()**, see [Starting UIAbility or ServiceExtensionAbility Across Devices (No Data Returned)](#starting-uiability-or-serviceextensionability-across-devices-no-data-returned). 557 5585. Sends agreed parcelable data to the CalleeAbility. 559 1. The parcelable data can be sent to the CalleeAbility with or without a return value. The method and parcelable data must be consistent with those of the CalleeAbility. The following example describes how to send data to the CalleeAbility. 560 561 ```ts 562 import UIAbility, { Caller } from '@ohos.app.ability.UIAbility'; 563 import { BusinessError } from '@ohos.base'; 564 const MSG_SEND_METHOD: string = 'CallSendMsg'; 565 export default class EntryAbility extends UIAbility { 566 // ... 567 caller: Caller | undefined; 568 async onButtonCall() { 569 try { 570 let msg: MyParcelable = new MyParcelable(1, 'origin_Msg'); 571 if (this.caller) { 572 await this.caller.call(MSG_SEND_METHOD, msg); 573 } 574 } catch (error) { 575 console.info(`caller call failed with ${error}`); 576 } 577 } 578 // ... 579 } 580 ``` 581 2. In the following, **CallWithResult** is used to send data **originMsg** to the CalleeAbility and assign the data processed by the **CallSendMsg** method to **backMsg**. 582 583 ```ts 584 import UIAbility, { Caller } from '@ohos.app.ability.UIAbility'; 585 import rpc from '@ohos.rpc'; 586 const MSG_SEND_METHOD: string = 'CallSendMsg'; 587 let originMsg: string = ''; 588 let backMsg: string = ''; 589 export default class EntryAbility extends UIAbility { 590 // ... 591 caller: Caller | undefined; 592 async onButtonCallWithResult(originMsg: string, backMsg: string) { 593 try { 594 let msg: MyParcelable = new MyParcelable(1, originMsg); 595 if (this.caller) { 596 const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg); 597 console.info('caller callWithResult succeed'); 598 let result: MyParcelable = new MyParcelable(0, ''); 599 data.readParcelable(result); 600 backMsg = result.str; 601 console.info(`caller result is [${result.num}, ${result.str}]`); 602 } 603 } catch (error) { 604 console.info(`caller callWithResult failed with ${error}`); 605 } 606 } 607 // ... 608 } 609 ``` 610 6116. Release the caller object. 612 613 When the caller object is no longer required, use **release()** to release it. 614 615 ```ts 616 import UIAbility, { Caller } from '@ohos.app.ability.UIAbility'; 617 export default class EntryAbility extends UIAbility { 618 caller: Caller | undefined; 619 releaseCall() { 620 try { 621 if (this.caller) { 622 this.caller.release(); 623 this.caller = undefined; 624 } 625 console.info('caller release succeed'); 626 } catch (error) { 627 console.info(`caller release failed with ${error}`); 628 } 629 } 630 } 631 ``` 632