• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![hop-multi-device-collaboration](figures/hop-multi-device-collaboration.png)
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