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