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 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 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 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: Want): Promise<Caller> | 启动指定UIAbility并获取其Caller通信接口,默认为后台启动,通过配置want可实现前台启动,详见[接口文档](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartabilitybycall)。AbilityContext与ServiceExtensionContext均支持该接口。 | 647| on(method: string, callback: CalleeCallBack): void | 通用组件Callee注册method对应的callback方法。 | 648| off(method: string): void | 通用组件Callee解注册method的callback方法。 | 649| call(method: string, data: rpc.Parcelable): Promise<void> | 向通用组件Callee发送约定序列化数据。 | 650| callWithResult(method: string, data: rpc.Parcelable): Promise<rpc.MessageSequence> | 向通用组件Callee发送约定序列化数据, 并将Callee返回的约定序列化数据带回。 | 651| release(): void | 释放通用组件的Caller通信接口。 | 652| on(type: "release", callback: OnReleaseCallback): 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)