• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 启动应用内的UIAbility组件
2
3
4[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)是系统调度的最小单元。在设备内的功能模块之间跳转时,会涉及到启动特定的UIAbility,包括应用内的其他UIAbility、或者其他应用的UIAbility(例如启动三方支付UIAbility)。
5
6
7本文主要介绍启动应用内的UIAbility组件的方式。应用间的组件跳转详见[应用间跳转](link-between-apps-overview.md)。<!--Del-->对于跨设备的应用组件交互,请参见[应用组件跨设备交互(流转)](inter-device-interaction-hop-overview.md)。<!--DelEnd-->
8
9
10- [启动应用内的UIAbility](#启动应用内的uiability)
11- [启动应用内的UIAbility并获取返回结果](#启动应用内的uiability并获取返回结果)
12- [启动UIAbility的指定页面](#启动uiability的指定页面)
13<!--Del-->
14- [启动UIAbility指定窗口模式(仅对系统应用开放)](#启动uiability指定窗口模式仅对系统应用开放)
15- [通过Call调用实现UIAbility交互(仅对系统应用开放)](#通过call调用实现uiability交互仅对系统应用开放)
16<!--DelEnd-->
17
18
19## 启动应用内的UIAbility
20
21当一个应用内包含多个[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)时,存在应用内启动UIAbility的场景。例如在支付应用中从入口UIAbility启动收付款UIAbility。
22
23假设应用中有两个UIAbility:EntryAbility和FuncAbility(可以在同一个Module中,也可以在不同的Module中),需要从EntryAbility的页面中启动FuncAbility。
24
251. 在EntryAbility中,通过调用[startAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability)方法启动UIAbility,[want](../reference/apis-ability-kit/js-apis-app-ability-want.md)为UIAbility实例启动的入口参数,其中bundleName为待启动应用的Bundle名称,abilityName为待启动的Ability名称,moduleName在待启动的UIAbility属于不同的Module时添加,parameters为自定义信息参数。示例中的context的获取方式请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)。
26
27    ```ts
28    import { common, Want } from '@kit.AbilityKit';
29    import { hilog } from '@kit.PerformanceAnalysisKit';
30    import { BusinessError } from '@kit.BasicServicesKit';
31
32    const TAG: string = '[Page_UIAbilityComponentsInteractive]';
33    const DOMAIN_NUMBER: number = 0xFF00;
34
35    @Entry
36    @Component
37    struct Page_UIAbilityComponentsInteractive {
38      private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
39
40      build() {
41        Column() {
42          //...
43          List({ initialIndex: 0 }) {
44            ListItem() {
45              Row() {
46                //...
47              }
48              .onClick(() => {
49                // context为Ability对象的成员,在非Ability对象内部调用需要
50                // 将Context对象传递过去
51                let wantInfo: Want = {
52                  deviceId: '', // deviceId为空表示本设备
53                  bundleName: 'com.samples.stagemodelabilitydevelop',
54                  moduleName: 'entry', // moduleName非必选
55                  abilityName: 'FuncAbilityA',
56                  parameters: {
57                    // 自定义信息
58                    info: '来自EntryAbility Page_UIAbilityComponentsInteractive页面'
59                  },
60                };
61                // context为调用方UIAbility的UIAbilityContext
62                this.context.startAbility(wantInfo).then(() => {
63                  hilog.info(DOMAIN_NUMBER, TAG, 'startAbility success.');
64                }).catch((error: BusinessError) => {
65                  hilog.error(DOMAIN_NUMBER, TAG, 'startAbility failed.');
66                });
67              })
68            }
69            //...
70          }
71          //...
72        }
73        //...
74      }
75    }
76    ```
77
782. 在FuncAbility的[onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate)或者[onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonnewwant)生命周期回调文件中接收EntryAbility传递过来的参数。
79
80    ```ts
81    import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
82
83    export default class FuncAbilityA extends UIAbility {
84      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
85        // 接收调用方UIAbility传过来的参数
86        let funcAbilityWant = want;
87        let info = funcAbilityWant?.parameters?.info;
88      }
89      //...
90    }
91    ```
92
93    > **说明:**
94    >
95    > 在被拉起的FuncAbility中,可以通过获取传递过来的[want](../reference/apis-ability-kit/js-apis-app-ability-want.md)参数的`parameters`来获取拉起方[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)的PID、Bundle Name等信息。
96
973. 在FuncAbility业务完成之后,如需要停止当前[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)实例,在FuncAbility中通过调用[terminateSelf()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateself)方法实现。
98
99    ```ts
100    import { common } from '@kit.AbilityKit';
101    import { hilog } from '@kit.PerformanceAnalysisKit';
102
103    const TAG: string = '[Page_FromStageModel]';
104    const DOMAIN_NUMBER: number = 0xFF00;
105
106    @Entry
107    @Component
108    struct Page_FromStageModel {
109      build() {
110        Column() {
111          //...
112          Button('FuncAbilityB')
113            .onClick(() => {
114              let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
115              // context为需要停止的UIAbility实例的AbilityContext
116              context.terminateSelf((err) => {
117                if (err.code) {
118                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`);
119                  return;
120                }
121              });
122            })
123        }
124        //...
125      }
126    }
127    ```
128
129    > **说明:**
130    >
131    > 调用terminateSelf()方法停止当前UIAbility实例时,默认会保留该实例的快照(Snapshot),即在最近任务列表中仍然能查看到该实例对应的任务。如不需要保留该实例的快照,可以在其对应UIAbility的[module.json5配置文件](../quick-start/module-configuration-file.md)中,将[abilities标签](../quick-start/module-configuration-file.md#abilities标签)的removeMissionAfterTerminate字段配置为true。
132
1334. 如需要关闭应用所有的[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)实例,可以调用[ApplicationContext](../reference/apis-ability-kit/js-apis-inner-application-applicationContext.md)的[killAllProcesses()](../reference/apis-ability-kit/js-apis-inner-application-applicationContext.md#applicationcontextkillallprocesses)方法实现关闭应用所有的进程。
134
135
136## 启动应用内的UIAbility并获取返回结果
137
138在一个EntryAbility启动另外一个FuncAbility时,希望在被启动的FuncAbility完成相关业务后,能将结果返回给调用方。例如在应用中将入口功能和账号登录功能分别设计为两个独立的[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md),在账号登录UIAbility中完成登录操作后,需要将登录的结果返回给入口UIAbility。
139
1401. 在EntryAbility中,调用[startAbilityForResult()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartabilityforresult-2)接口启动FuncAbility,异步回调中的data用于接收FuncAbility停止自身后返回给EntryAbility的信息。示例中的context的获取方式请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)。
141
142    ```ts
143    import { common, Want } from '@kit.AbilityKit';
144    import { hilog } from '@kit.PerformanceAnalysisKit';
145    import { BusinessError } from '@kit.BasicServicesKit';
146
147    const TAG: string = '[Page_UIAbilityComponentsInteractive]';
148    const DOMAIN_NUMBER: number = 0xFF00;
149
150    @Entry
151    @Component
152    struct Page_UIAbilityComponentsInteractive {
153      build() {
154        Column() {
155          //...
156          List({ initialIndex: 0 }) {
157            ListItem() {
158              Row() {
159                //...
160              }
161              .onClick(() => {
162                let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
163                const RESULT_CODE: number = 1001;
164                let want: Want = {
165                  deviceId: '', // deviceId为空表示本设备
166                  bundleName: 'com.samples.stagemodelabilitydevelop',
167                  moduleName: 'entry', // moduleName非必选
168                  abilityName: 'FuncAbilityA',
169                  parameters: {
170                    // 自定义信息
171                    info: '来自EntryAbility UIAbilityComponentsInteractive页面'
172                  }
173                };
174                context.startAbilityForResult(want).then((data) => {
175                  if (data?.resultCode === RESULT_CODE) {
176                    // 解析被调用方UIAbility返回的信息
177                    let info = data.want?.parameters?.info;
178                    hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(info) ?? '');
179                    if (info !== null) {
180                      this.getUIContext().getPromptAction().showToast({
181                        message: JSON.stringify(info)
182                      });
183                    }
184                  }
185                  hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(data.resultCode) ?? '');
186                }).catch((err: BusinessError) => {
187                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability for result. Code is ${err.code}, message is ${err.message}`);
188                });
189              })
190            }
191            //...
192          }
193          //...
194        }
195        //...
196      }
197    }
198    ```
199
2002. 在FuncAbility停止自身时,需要调用[terminateSelfWithResult()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateselfwithresult)方法,入参[abilityResult](../reference/apis-ability-kit/js-apis-inner-ability-abilityResult.md)为FuncAbility需要返回给EntryAbility的信息。
201
202    ```ts
203    import { common } from '@kit.AbilityKit';
204    import { hilog } from '@kit.PerformanceAnalysisKit';
205
206    const TAG: string = '[Page_FuncAbilityA]';
207    const DOMAIN_NUMBER: number = 0xFF00;
208
209    @Entry
210    @Component
211    struct Page_FuncAbilityA {
212      build() {
213        Column() {
214          //...
215          List({ initialIndex: 0 }) {
216            ListItem() {
217              Row() {
218                //...
219              }
220              .onClick(() => {
221                let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
222                const RESULT_CODE: number = 1001;
223                let abilityResult: common.AbilityResult = {
224                  resultCode: RESULT_CODE,
225                  want: {
226                    bundleName: 'com.samples.stagemodelabilitydevelop',
227                    moduleName: 'entry', // moduleName非必选
228                    abilityName: 'FuncAbilityB',
229                    parameters: {
230                      info: '来自FuncAbility Index页面'
231                    },
232                  },
233                };
234                context.terminateSelfWithResult(abilityResult, (err) => {
235                  if (err.code) {
236                    hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self with result. Code is ${err.code}, message is ${err.message}`);
237                    return;
238                  }
239                });
240              })
241            }
242            //...
243          }
244          //...
245        }
246        //...
247      }
248    }
249    ```
250
2513. FuncAbility停止自身后,EntryAbility通过[startAbilityForResult()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartabilityforresult-2)方法回调接收被FuncAbility返回的信息,RESULT_CODE需要与前面的数值保持一致。
252
253    ```ts
254    import { common, Want } from '@kit.AbilityKit';
255    import { hilog } from '@kit.PerformanceAnalysisKit';
256    import { BusinessError } from '@kit.BasicServicesKit';
257
258    const TAG: string = '[Page_UIAbilityComponentsInteractive]';
259    const DOMAIN_NUMBER: number = 0xFF00;
260
261    @Entry
262    @Component
263    struct Page_UIAbilityComponentsInteractive {
264      build() {
265        Column() {
266          //...
267          List({ initialIndex: 0 }) {
268            ListItem() {
269              Row() {
270                //...
271              }
272              .onClick(() => {
273                let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
274                const RESULT_CODE: number = 1001;
275
276                let want: Want = {
277                  deviceId: '', // deviceId为空表示本设备
278                  bundleName: 'com.samples.stagemodelabilitydevelop',
279                  moduleName: 'entry', // moduleName非必选
280                  abilityName: 'FuncAbilityA',
281                  parameters: {
282                    // 自定义信息
283                    info: '来自EntryAbility UIAbilityComponentsInteractive页面'
284                  }
285                };
286                context.startAbilityForResult(want).then((data) => {
287                  if (data?.resultCode === RESULT_CODE) {
288                    // 解析被调用方UIAbility返回的信息
289                    let info = data.want?.parameters?.info;
290                    hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(info) ?? '');
291                    if (info !== null) {
292                      this.getUIContext().getPromptAction().showToast({
293                        message: JSON.stringify(info)
294                      });
295                    }
296                  }
297                  hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(data.resultCode) ?? '');
298                }).catch((err: BusinessError) => {
299                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability for result. Code is ${err.code}, message is ${err.message}`);
300                });
301              })
302            }
303            //...
304          }
305          //...
306        }
307        //...
308      }
309    }
310    ```
311
312
313## 启动UIAbility的指定页面
314
315### 概述
316
317一个[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)可以对应多个页面,在不同的场景下启动该UIAbility时需要展示不同的页面,例如从一个UIAbility的页面中跳转到另外一个UIAbility时,希望启动目标UIAbility的指定页面。
318
319UIAbility的启动分为两种情况:UIAbility冷启动和UIAbility热启动。
320
321- UIAbility冷启动:指的是UIAbility实例处于完全关闭状态下被启动,这需要完整地加载和初始化UIAbility实例的代码、资源等。
322- UIAbility热启动:指的是UIAbility实例已经启动并在前台运行过,由于某些原因切换到后台,再次启动该UIAbility实例,这种情况下可以快速恢复UIAbility实例的状态。
323
324本文主要讲解[目标UIAbility冷启动](#目标uiability冷启动)和[目标UIAbility热启动](#目标uiability热启动)两种启动指定页面的场景,以及在讲解启动指定页面之前会讲解到在调用方如何指定启动页面。
325
326
327### 调用方UIAbility指定启动页面
328
329调用方[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)启动另外一个UIAbility时,通常需要跳转到指定的页面。例如FuncAbility包含两个页面(Index对应首页,Second对应功能A页面),此时需要在传入的[want](../reference/apis-ability-kit/js-apis-app-ability-want.md)参数中配置指定的页面路径信息,可以通过want中的parameters参数增加一个自定义参数传递页面跳转信息。示例中的context的获取方式请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)。
330
331
332```ts
333import { common, Want } from '@kit.AbilityKit';
334import { hilog } from '@kit.PerformanceAnalysisKit';
335import { BusinessError } from '@kit.BasicServicesKit';
336
337const TAG: string = '[Page_UIAbilityComponentsInteractive]';
338const DOMAIN_NUMBER: number = 0xFF00;
339
340@Entry
341@Component
342struct Page_UIAbilityComponentsInteractive {
343  build() {
344    Column() {
345      //...
346      List({ initialIndex: 0 }) {
347        ListItem() {
348          Row() {
349            //...
350          }
351          .onClick(() => {
352            let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
353            let want: Want = {
354              deviceId: '', // deviceId为空表示本设备
355              bundleName: 'com.samples.stagemodelabilityinteraction',
356              moduleName: 'entry', // moduleName非必选
357              abilityName: 'FuncAbility',
358              parameters: { // 自定义参数传递页面信息
359                router: 'funcA'
360              }
361            };
362            // context为调用方UIAbility的UIAbilityContext
363            context.startAbility(want).then(() => {
364              hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting ability.');
365            }).catch((err: BusinessError) => {
366              hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability. Code is ${err.code}, message is ${err.message}`);
367            });
368          })
369        }
370        //...
371      }
372      //...
373    }
374    //...
375  }
376}
377```
378
379
380### 目标UIAbility冷启动
381
382目标[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)冷启动时,在目标UIAbility的[onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate)生命周期回调中,接收调用方传过来的参数。然后在目标UIAbility的[onWindowStageCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonwindowstagecreate)生命周期回调中,解析调用方传递过来的[want](../reference/apis-ability-kit/js-apis-app-ability-want.md)参数,获取到需要加载的页面信息url,传入[windowStage.loadContent()](../reference/apis-arkui/js-apis-window.md#loadcontent9)方法。
383
384
385```ts
386import { AbilityConstant, Want, UIAbility } from '@kit.AbilityKit';
387import { hilog } from '@kit.PerformanceAnalysisKit';
388import { window, UIContext } from '@kit.ArkUI';
389
390const DOMAIN_NUMBER: number = 0xFF00;
391const TAG: string = '[EntryAbility]';
392
393export default class EntryAbility extends UIAbility {
394  funcAbilityWant: Want | undefined = undefined;
395  uiContext: UIContext | undefined = undefined;
396
397  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
398    // 接收调用方UIAbility传过来的参数
399    this.funcAbilityWant = want;
400  }
401
402  onWindowStageCreate(windowStage: window.WindowStage): void {
403    // Main window is created, set main page for this ability
404    hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onWindowStageCreate');
405    // Main window is created, set main page for this ability
406    let url = 'pages/Index';
407    if (this.funcAbilityWant?.parameters?.router && this.funcAbilityWant.parameters.router === 'funcA') {
408      url = 'pages/Page_ColdStartUp';
409    }
410    windowStage.loadContent(url, (err, data) => {
411      // ...
412    });
413  }
414}
415```
416
417### 目标UIAbility热启动
418
419在应用开发中,会遇到目标[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)实例之前已经启动过的场景,这时再次启动目标UIAbility时,不会重新走初始化逻辑,只会直接触发[onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonnewwant)生命周期方法。为了实现跳转到指定页面,需要在onNewWant()中解析参数进行处理。
420
421例如短信应用和联系人应用配合使用的场景。
422
4231. 用户先打开短信应用,短信应用的UIAbility实例启动,显示短信应用的主页。
4242. 用户将设备回到桌面界面,短信应用进入后台运行状态。
4253. 用户打开联系人应用,找到联系人张三。
4264. 用户点击联系人张三的短信按钮,会重新启动短信应用的UIAbility实例。
4275. 由于短信应用的UIAbility实例已经启动过了,此时会触发该UIAbility的onNewWant()回调,而不会再走[onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate)和[onWindowStageCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonwindowstagecreate)等初始化逻辑。
428
429图1 目标UIAbility热启动
430![](figures/uiability-hot-start.png)
431
432开发步骤如下所示。
433
4341. 冷启动短信应用的UIAbility实例时,在[onWindowStageCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonwindowstagecreate)生命周期回调中,通过调用[getUIContext()](../reference/apis-arkui/js-apis-window.md#getuicontext10)接口获取UI上下文实例[UIContext](../reference/apis-arkui/js-apis-arkui-UIContext.md)对象。
435
436    ```ts
437    import { hilog } from '@kit.PerformanceAnalysisKit';
438    import { Want, UIAbility } from '@kit.AbilityKit';
439    import { window, UIContext } from '@kit.ArkUI';
440
441    const DOMAIN_NUMBER: number = 0xFF00;
442    const TAG: string = '[EntryAbility]';
443
444    export default class EntryAbility extends UIAbility {
445      funcAbilityWant: Want | undefined = undefined;
446      uiContext: UIContext | undefined = undefined;
447
448      // ...
449
450      onWindowStageCreate(windowStage: window.WindowStage): void {
451        // Main window is created, set main page for this ability
452        hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onWindowStageCreate');
453        let url = 'pages/Index';
454        if (this.funcAbilityWant?.parameters?.router && this.funcAbilityWant.parameters.router === 'funcA') {
455          url = 'pages/Page_ColdStartUp';
456        }
457
458        windowStage.loadContent(url, (err, data) => {
459          if (err.code) {
460            return;
461          }
462
463          let windowClass: window.Window;
464          windowStage.getMainWindow((err, data) => {
465            if (err.code) {
466              hilog.error(DOMAIN_NUMBER, TAG, `Failed to obtain the main window. Code is ${err.code}, message is ${err.message}`);
467              return;
468            }
469            windowClass = data;
470            this.uiContext = windowClass.getUIContext();
471          });
472          hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
473        });
474      }
475    }
476    ```
477
4782. 在短信应用UIAbility的[onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonnewwant)回调中解析调用方传递过来的[want](../reference/apis-ability-kit/js-apis-app-ability-want.md)参数,通过调用[UIContext](../reference/apis-arkui/js-apis-arkui-UIContext.md)中的[getRouter()](../reference/apis-arkui/js-apis-arkui-UIContext.md#getrouter)方法获取[Router](../reference/apis-arkui/js-apis-arkui-UIContext.md#router)对象,并进行指定页面的跳转。此时再次启动该短信应用的UIAbility实例时,即可跳转到该短信应用的UIAbility实例的指定页面。
479
480    ```ts
481    import { AbilityConstant, Want, UIAbility } from '@kit.AbilityKit';
482    import { hilog } from '@kit.PerformanceAnalysisKit';
483    import type { Router, UIContext } from '@kit.ArkUI';
484    import type { BusinessError } from '@kit.BasicServicesKit';
485
486    const DOMAIN_NUMBER: number = 0xFF00;
487    const TAG: string = '[EntryAbility]';
488
489    export default class EntryAbility extends UIAbility {
490      funcAbilityWant: Want | undefined = undefined;
491      uiContext: UIContext | undefined = undefined;
492      // ...
493      onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
494        if (want?.parameters?.router && want.parameters.router === 'funcA') {
495          let funcAUrl = 'pages/Page_HotStartUp';
496          if (this.uiContext) {
497            let router: Router = this.uiContext.getRouter();
498            router.pushUrl({
499              url: funcAUrl
500            }).catch((err: BusinessError) => {
501              hilog.error(DOMAIN_NUMBER, TAG, `Failed to push url. Code is ${err.code}, message is ${err.message}`);
502            });
503          }
504        }
505      }
506    }
507    ```
508
509> **说明:**
510>
511> 当被调用方[UIAbility组件启动模式](uiability-launch-type.md)设置为multiton启动模式时,每次启动都会创建一个新的实例,那么onNewWant()回调就不会被用到。
512
513<!--Del-->
514## 启动UIAbility指定窗口模式(仅对系统应用开放)
515
516当用户打开应用时,应用程序会以不同的窗口模式进行展示,即启动[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)的窗口模式。应用程序可以启动为全屏模式,悬浮窗模式或分屏模式。
517
518全屏模式是指应用程序启动后,占据整个屏幕,用户无法同时查看其他窗口或应用程序。全屏模式通常适用于那些要求用户专注于特定任务或界面的应用程序。
519
520悬浮窗模式是指应用程序启动后,以浮动窗口的形式显示在屏幕上,用户可以轻松切换到其他窗口或应用程序。悬浮窗通常适用于需要用户同时处理多个任务的应用程序。
521
522分屏模式允许用户在同一屏幕上同时运行两个应用程序,其中一个应用程序占据屏幕左侧/上侧的一部分,另一个应用程序占据右侧/下侧的一部分。分屏模式主要用于提高用户的多任务处理效率。
523
524使用[startAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability)方法启动UIAbility时,可以通过在入参中增加[StartOptions](../reference/apis-ability-kit/js-apis-app-ability-startOptions.md)参数的windowMode属性来配置启动UIAbility的窗口模式。
525
526> **说明:**
527>
528> 1. 如果在使用startAbility()方法启动UIAbility时,入参中未指定StartOptions参数的windowMode属性,那么UIAbility将以系统默认的窗口展示形态启动。
529> 2. 为了确保启动的UIAbility展示形态能够被支持,需要在该UIAbility对应的[module.json5配置文件](../quick-start/module-configuration-file.md)中[abilities标签](../quick-start/module-configuration-file.md#abilities标签)的supportWindowMode字段确认启动的展示形态被支持。
530
531以下是具体的操作步骤,以悬浮窗模式为例,假设需要从EntryAbility的页面中启动FuncAbility:
532
5331. 在调用startAbility()方法时,增加StartOptions参数。
5342. 在StartOptions参数中设置windowMode字段为WINDOW_MODE_FLOATING,表示启动的UIAbility将以悬浮窗的形式展示。
5353. windowMode属性仅适用于系统应用,三方应用可以使用displayId属性。
536
537示例中的context的获取方式请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)。
538
539```ts
540import { AbilityConstant, common, Want, StartOptions } from '@kit.AbilityKit';
541import { hilog } from '@kit.PerformanceAnalysisKit';
542import { BusinessError } from '@kit.BasicServicesKit';
543
544const TAG: string = '[Page_UIAbilityComponentsInteractive]';
545const DOMAIN_NUMBER: number = 0xFF00;
546
547@Entry
548@Component
549struct Page_UIAbilityComponentsInteractive {
550  build() {
551    Column() {
552      //...
553      List({ initialIndex: 0 }) {
554        ListItem() {
555          Row() {
556            //...
557          }
558          .onClick(() => {
559            let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
560            let want: Want = {
561              deviceId: '', // deviceId为空表示本设备
562              bundleName: 'com.samples.stagemodelabilitydevelop',
563              moduleName: 'entry', // moduleName非必选
564              abilityName: 'FuncAbilityB',
565              parameters: {
566                // 自定义信息
567                info: '来自EntryAbility Index页面'
568              }
569            };
570            let options: StartOptions = {
571              windowMode: AbilityConstant.WindowMode.WINDOW_MODE_FLOATING
572            };
573            // context为调用方UIAbility的UIAbilityContext
574            context.startAbility(want, options).then(() => {
575              hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting ability.');
576            }).catch((err: BusinessError) => {
577              hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability. Code is ${err.code}, message is ${err.message}`);
578            });
579          })
580        }
581        //...
582      }
583      //...
584    }
585    //...
586  }
587}
588```
589
590效果示意如下图所示。
591
592![](figures/start-uiability-floating-window.png)
593
594
595## 通过Call调用实现UIAbility交互(仅对系统应用开放)
596
597Call调用是[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)能力的扩展,它为UIAbility提供一种能够被外部调用并与外部进行通信的能力。Call调用支持前台与后台两种启动方式,使UIAbility既能被拉起到前台展示UI,也可以在后台被创建并运行。Call调用在调用方与被调用方间建立了IPC通信,因此应用开发者可通过Call调用实现不同UIAbility之间的数据共享。
598
599Call调用的核心接口是[startAbilityByCall()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartabilitybycall)方法,与[startAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability)接口的不同之处在于:
600
601- startAbilityByCall支持前台与后台两种启动方式,而startAbility()仅支持前台启动。
602
603- 调用方可使用startAbilityByCall()所返回的[Caller](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#caller)对象与被调用方进行通信,而startAbility()不具备通信能力。
604
605Call调用的使用场景主要包括:
606
607- 需要与被启动的UIAbility进行通信。
608
609- 希望被启动的UIAbility在后台运行。
610
611
612**表1** Call调用相关名词解释
613
614| 名词 | 描述 |
615| -------- | -------- |
616| CallerAbility | 进行Call调用的UIAbility(调用方)。 |
617| CalleeAbility | 被Call调用的UIAbility(被调用方)。 |
618| Caller | 实际对象,由startAbilityByCall接口返回,CallerAbility可使用Caller与CalleeAbility进行通信。 |
619| Callee | 实际对象,被CalleeAbility持有,可与Caller进行通信。 |
620
621Call调用示意图如下所示。
622
623**图1** Call调用示意图
624![call](figures/call.png)
625
626- CallerAbility调用[startAbilityByCall()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartabilitybycall)接口获取[Caller](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#caller),并使用Caller对象的[call](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#callercall)方法向CalleeAbility发送数据。
627
628- CalleeAbility持有一个[Callee](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#callee)对象,通过Callee的[on](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#calleeon)方法注册回调函数,当接收到Caller发送的数据时将会调用对应的回调函数。
629
630> **说明:**
631> 1. 当前仅支持系统应用使用Call调用。
632>
633> 2. CalleeAbility的启动模式需要为单实例。
634>
635> 3. Call调用既支持本地(设备内)Call调用,也支持跨设备Call调用,下面介绍设备内Call调用方法。跨设备Call调用方法请参见[跨设备Call调用](hop-multi-device-collaboration.md#通过跨设备call调用实现多端协同)。
636
637
638### 接口说明
639
640Call功能主要接口如下表所示。具体的API详见[接口文档](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#caller)。
641
642**表2** Call功能主要接口
643
644| 接口名 | 描述 |
645| -------- | -------- |
646| startAbilityByCall(want:&nbsp;Want):&nbsp;Promise&lt;Caller&gt; | 启动指定UIAbility并获取其Caller通信接口,默认为后台启动,通过配置want可实现前台启动,详见[接口文档](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartabilitybycall)。AbilityContext与ServiceExtensionContext均支持该接口。 |
647| on(method:&nbsp;string,&nbsp;callback:&nbsp;CalleeCallBack):&nbsp;void | 通用组件Callee注册method对应的callback方法。 |
648| off(method:&nbsp;string):&nbsp;void | 通用组件Callee解注册method的callback方法。 |
649| call(method:&nbsp;string,&nbsp;data:&nbsp;rpc.Parcelable):&nbsp;Promise&lt;void&gt; | 向通用组件Callee发送约定序列化数据。 |
650| callWithResult(method:&nbsp;string,&nbsp;data:&nbsp;rpc.Parcelable):&nbsp;Promise&lt;rpc.MessageSequence&gt; | 向通用组件Callee发送约定序列化数据,&nbsp;并将Callee返回的约定序列化数据带回。 |
651| release():&nbsp;void | 释放通用组件的Caller通信接口。 |
652| on(type:&nbsp;"release",&nbsp;callback:&nbsp;OnReleaseCallback):&nbsp;void | 注册通用组件通信断开监听通知。 |
653
654设备内通过Call调用实现[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)交互,涉及如下两部分开发:
655
656- [创建Callee被调用端](#开发步骤创建callee被调用端)
657
658- [访问Callee被调用端](#开发步骤访问callee被调用端)
659
660
661### 开发步骤(创建Callee被调用端)
662
663在[Callee](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#callee)被调用端,需要实现指定方法的数据接收回调函数、数据的序列化及反序列化方法。在需要接收数据期间,通过[on](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#calleeon)接口注册监听,无需接收数据时通过[off](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#calleeoff)接口解除监听。
664
6651. 配置[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)的启动模式。
666
667   例如将CalleeAbility配置为单实例模式`singleton`,配置方式请参见[UIAbility组件启动模式](uiability-launch-type.md)。
668
6692. 导入UIAbility模块。
670
671   ```ts
672   import { UIAbility } from '@kit.AbilityKit';
673   ```
674
6753. 定义约定的序列化数据。
676   调用端及被调用端发送接收的数据格式需协商一致,如下示例约定数据由number和string组成。
677
678
679    ```ts
680    import { rpc } from '@kit.IPCKit';
681
682    class MyParcelable {
683      num: number = 0;
684      str: string = '';
685
686      constructor(num: number, string: string) {
687        this.num = num;
688        this.str = string;
689      }
690
691      mySequenceable(num: number, string: string): void {
692        this.num = num;
693        this.str = string;
694      }
695
696      marshalling(messageSequence: rpc.MessageSequence): boolean {
697        messageSequence.writeInt(this.num);
698        messageSequence.writeString(this.str);
699        return true;
700      }
701
702      unmarshalling(messageSequence: rpc.MessageSequence): boolean {
703        this.num = messageSequence.readInt();
704        this.str = messageSequence.readString();
705        return true;
706      }
707    }
708    ```
709
7104. 实现[Callee.on](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#calleeon)监听及[Callee.off](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#calleeoff)解除监听。
711
712   被调用端[Callee](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#callee)的监听函数注册时机,取决于应用开发者。注册监听之前的数据不会被处理,取消监听之后的数据不会被处理。如下示例在[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)的[onCreate](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate)注册'MSG_SEND_METHOD'监听,在[onDestroy](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityondestroy)取消监听,收到序列化数据后作相应处理并返回,应用开发者根据实际需要做相应处理。具体示例代码如下:
713
714
715    ```ts
716    import { AbilityConstant, UIAbility, Want, Caller } from '@kit.AbilityKit';
717    import { hilog } from '@kit.PerformanceAnalysisKit';
718    import { rpc } from '@kit.IPCKit';
719
720    const MSG_SEND_METHOD: string = 'CallSendMsg';
721    const DOMAIN_NUMBER: number = 0xFF00;
722    const TAG: string = '[CalleeAbility]';
723
724    class MyParcelable {
725      num: number = 0;
726      str: string = '';
727
728      constructor(num: number, string: string) {
729        this.num = num;
730        this.str = string;
731      }
732
733      mySequenceable(num: number, string: string): void {
734        this.num = num;
735        this.str = string;
736      }
737
738      marshalling(messageSequence: rpc.MessageSequence): boolean {
739        messageSequence.writeInt(this.num);
740        messageSequence.writeString(this.str);
741        return true;
742      }
743
744      unmarshalling(messageSequence: rpc.MessageSequence): boolean {
745        this.num = messageSequence.readInt();
746        this.str = messageSequence.readString();
747        return true;
748      }
749    }
750
751    function sendMsgCallback(data: rpc.MessageSequence): rpc.Parcelable {
752      hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'CalleeSortFunc called');
753
754      // 获取Caller发送的序列化数据
755      let receivedData: MyParcelable = new MyParcelable(0, '');
756      data.readParcelable(receivedData);
757      hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `receiveData[${receivedData.num}, ${receivedData.str}]`);
758      let num: number = receivedData.num;
759
760      // 作相应处理
761      // 返回序列化数据result给Caller
762      return new MyParcelable(num + 1, `send ${receivedData.str} succeed`) as rpc.Parcelable;
763    }
764
765    export default class CalleeAbility extends UIAbility {
766      caller: Caller | undefined;
767
768      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
769        try {
770          this.callee.on(MSG_SEND_METHOD, sendMsgCallback);
771        } catch (error) {
772          hilog.error(DOMAIN_NUMBER, TAG, '%{public}s', `Failed to register. Error is ${error}`);
773        }
774      }
775
776      releaseCall(): void {
777        try {
778          if (this.caller) {
779            this.caller.release();
780            this.caller = undefined;
781          }
782          hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'caller release succeed');
783        } catch (error) {
784          hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `caller release failed with ${error}`);
785        }
786      }
787
788      onDestroy(): void {
789        try {
790          this.callee.off(MSG_SEND_METHOD);
791          hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Callee OnDestroy');
792          this.releaseCall();
793        } catch (error) {
794          hilog.error(DOMAIN_NUMBER, TAG, '%{public}s', `Failed to register. Error is ${error}`);
795        }
796      }
797    }
798    ```
799
800
801### 开发步骤(访问Callee被调用端)
802
8031. 导入[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)模块。
804
805    ```ts
806    import { UIAbility } from '@kit.AbilityKit';
807    ```
808
8092. 获取[Caller](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#caller)通信接口。
810
811   [UIAbilityContext](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md)属性实现了[startAbilityByCall](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartabilitybycall)方法,用于获取指定通用组件的Caller通信接口。如下示例通过this.context获取UIAbility实例的context属性,使用startAbilityByCall拉起[Callee](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#callee)被调用端并获取Caller通信接口,注册Caller的[onRelease](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#calleronrelease)监听。应用开发者根据实际需要做相应处理。
812
813
814    ```ts
815    import { common, Want, Caller } from '@kit.AbilityKit';
816    import { hilog } from '@kit.PerformanceAnalysisKit';
817    import { BusinessError } from '@kit.BasicServicesKit';
818
819    const TAG: string = '[Page_UIAbilityComponentsInteractive]';
820    const DOMAIN_NUMBER: number = 0xFF00;
821
822    @Entry
823    @Component
824    struct Page_UIAbilityComponentsInteractive {
825      caller: Caller | undefined = undefined;
826
827      // 注册caller的release监听
828      private regOnRelease(caller: Caller): void {
829        hilog.info(DOMAIN_NUMBER, TAG, `caller is ${caller}`);
830        try {
831          caller.on('release', (msg: string) => {
832            hilog.info(DOMAIN_NUMBER, TAG, `caller onRelease is called ${msg}`);
833          })
834          hilog.info(DOMAIN_NUMBER, TAG, 'succeeded in registering on release.');
835        } catch (err) {
836          let code = (err as BusinessError).code;
837          let message = (err as BusinessError).message;
838          hilog.error(DOMAIN_NUMBER, TAG, `Failed to caller register on release. Code is ${code}, message is ${message}`);
839        }
840      };
841
842      build() {
843        Column() {
844          // ...
845          List({ initialIndex: 0 }) {
846            // ...
847            ListItem() {
848              Row() {
849                // ...
850              }
851              .onClick(() => {
852                let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
853                let want: Want = {
854                  bundleName: 'com.samples.stagemodelabilityinteraction',
855                  abilityName: 'CalleeAbility',
856                  parameters: {
857                    // 自定义信息
858                    info: 'CallSendMsg'
859                  }
860                };
861                context.startAbilityByCall(want).then((caller: Caller) => {
862                  hilog.info(DOMAIN_NUMBER, TAG, `Succeeded in starting ability.Code is ${caller}`);
863                  if (caller === undefined) {
864                    hilog.info(DOMAIN_NUMBER, TAG, 'get caller failed');
865                    return;
866                  }
867                  else {
868                    hilog.info(DOMAIN_NUMBER, TAG, 'get caller success');
869                    this.regOnRelease(caller);
870                    this.getUIContext().getPromptAction().showToast({
871                      message: 'CallerSuccess'
872                    });
873                    try {
874                      caller.release();
875                    } catch (releaseErr) {
876                      console.log('Caller.release catch error, error.code: ' + JSON.stringify(releaseErr.code) +
877                        ' error.message: ' + JSON.stringify(releaseErr.message));
878                    }
879                  }
880                }).catch((err: BusinessError) => {
881                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability. Code is ${err.code}, message is ${err.message}`);
882                });
883              })
884            }
885            // ...
886          }
887          // ...
888        }
889        // ...
890      }
891    }
892    ```
893<!--DelEnd-->
894
895## 相关实例
896
897针对UIAbility组件间交互开发,有以下相关实例可供参考:
898
899- [UIAbility内和UIAbility间页面的跳转(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/Ability/StageAbility)
900- [UIAbility内页面间的跳转(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/Ability/PagesRouter)