UIAbility是系统调度的最小单元。在设备内的功能模块之间跳转时,会涉及到启动特定的UIAbility,该UIAbility可以是应用内的其他UIAbility,也可以是其他应用的UIAbility(例如启动三方支付UIAbility)。
启动应用内的UIAbility
当一个应用内包含多个UIAbility时,存在应用内启动UIAbility的场景。例如在支付应用中从入口UIAbility启动收付款UIAbility。
假设应用中有两个UIAbility:EntryAbility和FuncAbility(可以在同一个Module中,也可以在不同的Module中),需要从EntryAbility的页面中启动FuncAbility。
1.代码结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
./entry/src/main/ ├── ets │ ├── entryability │ │ ├── EntryAbility.ets │ │ └── FuncAbilityA.ets │ ├── entrybackupability │ │ └── EntryBackupAbility.ets │ └── pages │ ├── FuncAIndex.ets │ └── Index.ets ├── module.json5 └── resources ├── base │ ├── element │ │ ├── color.json │ │ └── string.json │ ├── media │ │ ├── background.png │ │ ├── foreground.png │ │ ├── layered_image.json │ │ └── startIcon.png │ └── profile │ ├── backup_config.json │ └── main_pages.json ├── en_US │ └── element │ └── string.json ├── rawfile └── zh_CN └── element └── string.json |
2.在页面Index.ets启动UIAbility
在EntryAbility中,通过调用startAbility()方法启动UIAbility,want为UIAbility实例启动的入口参数,其中bundleName为待启动应用的Bundle名称,abilityName为待启动的Ability名称,moduleName在待启动的UIAbility属于不同的Module时添加,parameters为自定义信息参数。示例中的context的获取方式请参见获取UIAbility的上下文信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import { common, Want } from '@kit.AbilityKit' import { hilog } from '@kit.PerformanceAnalysisKit' import { BusinessError } from '@kit.BasicServicesKit' const DOMAIN_NUMBER: number = 0xFF00 const TAG: string = '[Page_Index]' @Entry @Component struct Index { private context = getContext(this) as common.UIAbilityContext build() { Row() { Column() { Button('Open FuncAbility') .onClick(() => { // context为Ability对象的成员,在非Ability对象内部调用需要将Context对象传递过去 let wantInfo: Want = { deviceId: '', bundleName: 'com.example.uiabilitycomponentsinteractive01', moduleName: 'entry', abilityName: 'FuncAbilityA', parameters: { info: '来自EntryAbility Index页面' } } // context为调用方UIAbility的UIAbilityContext this.context.startAbility(wantInfo).then(() => { hilog.info(DOMAIN_NUMBER, TAG, 'startAbility success.') }).catch((error: BusinessError) => { hilog.info(DOMAIN_NUMBER, TAG, 'startAbility failed.') }) }) } .width('100%') } .height('100%') } } |
3.在FuncAbilityA.ets中接收
在FuncAbility的onCreate()或者onNewWant()生命周期回调文件中接收EntryAbility传递过来的参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { window } from '@kit.ArkUI'; const DOMAIN_NUMBER: number = 0xFF00 const TAG: string = '[FuncAbilityA]' export default class FuncAbilityA extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); // 接收调用方UIAbility传过来的参数 let funcAbilityWant = want let info = funcAbilityWant.parameters?.info hilog.info(DOMAIN_NUMBER, TAG, info + '') } // ... } |
说明
在被拉起的FuncAbility中,可以通过获取传递过来的want参数的parameters来获取拉起方UIAbility的PID、Bundle Name等信息。
4.在FuncAIndex中操作停止UIAbility实例
- 在FuncAbility业务完成之后,如需要停止当前UIAbility实例,在FuncAbility中通过调用terminateSelf()方法实现。
- 如需要关闭应用所有的UIAbility实例,可以调用ApplicationContext的killAllProcesses()方法实现关闭应用所有的进程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
import { common } from '@kit.AbilityKit' import { hilog } from '@kit.PerformanceAnalysisKit' const DOMAIN_NUMBER: number = 0xFF00 const TAG: string = '[Page_FuncAIndex]' @Entry @Component struct FuncAIndex { build() { Row() { Column({ space: 20 }) { Button('停止当前UIAbility实例') .onClick(() => { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext context.terminateSelf((err) => { if (err.code) { hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`) return } }) }) Button('关闭所有UIAbility实例') .onClick(() => { let applicationContext: common.ApplicationContext = getContext(this) as common.ApplicationContext applicationContext.killAllProcesses(err => { if (err) { hilog.info(DOMAIN_NUMBER, TAG, `Failed to killAllProcesses. Code is ${err.code}, message is ${err.message}`) } }) }) } .width('100%') } .height('100%') } } |
说明
调用terminateSelf()
方法停止当前UIAbility实例时,默认会保留该实例的快照(Snapshot),即在最近任务列表中仍然能查看到该实例对应的任务。如不需要保留该实例的快照,可以在其对应UIAbility的module.json5配置文件中,将abilities标签的removeMissionAfterTerminate
字段配置为true
。
5.GIF演示
6.下载源码
启动应用内的UIAbility并获取返回结果
在一个EntryAbility启动另外一个FuncAbility时,希望在被启动的FuncAbility完成相关业务后,能将结果返回给调用方。例如在应用中将入口功能和帐号登录功能分别设计为两个独立的UIAbility,在帐号登录UIAbility中完成登录操作后,需要将登录的结果返回给入口UIAbility。
1.Index.ets
在EntryAbility中,调用startAbilityForResult()接口启动FuncAbility,异步回调中的data用于接收FuncAbility停止自身后返回给EntryAbility的信息。示例中的context的获取方式请参见获取UIAbility的上下文信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
import { common, Want } from '@kit.AbilityKit' import { hilog } from '@kit.PerformanceAnalysisKit' import { promptAction } from '@kit.ArkUI' import { BusinessError } from '@kit.BasicServicesKit' const DOMAIN_NUMBER: number = 0xFF00 const TAG: string = '[Page_Index]' @Entry @Component struct Index { build() { RelativeContainer() { Button("启动FuncAbility") .alignRules({ center: { anchor: '__container__', align: VerticalAlign.Center }, middle: { anchor: '__container__', align: HorizontalAlign.Center } }) .onClick(() => { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext const RESULT_CODE: number = 1001 let want: Want = { deviceId: '', bundleName: 'com.example.uiabilitycomponentsinteractive02', moduleName: 'entry', abilityName: 'FuncAbility', parameters: { info: '来自EntryAbility Index页面' } } context.startAbilityForResult(want).then(data => { if (data.resultCode === RESULT_CODE) { // 解析被调用方UIAbility返回的信息 let info = data.want?.parameters?.info hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(info) ?? '') if (info !== null) { promptAction.showToast({ message: JSON.stringify(info) }) } } hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(data.resultCode) ?? '') }).catch((err: BusinessError) => { hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability for result. Code is ${err.code}, message is ${err.message}`); }) }) } .height('100%') .width('100%') } } |
说明
FuncAbility停止自身后,EntryAbility通过startAbilityForResult()方法回调接收被FuncAbility返回的信息,RESULT_CODE需要与前面的数值保持一致。
2.FuncIndex.ets
在FuncAbility停止自身时,需要调用terminateSelfWithResult()方法,入参abilityResult为FuncAbility需要返回给EntryAbility的信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
import { common, Want } from '@kit.AbilityKit' import { hilog } from '@kit.PerformanceAnalysisKit' const DOMAIN_NUMBER: number = 0xFF00 const TAG: string = '[Page_FuncIndex]' @Entry @Component struct FuncIndex { build() { RelativeContainer() { Button("关闭当前页") .alignRules({ center: { anchor: '__container__', align: VerticalAlign.Center }, middle: { anchor: '__container__', align: HorizontalAlign.Center } }) .onClick(() => { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext const RESULT_CODE: number = 1001 let abilityResult: common.AbilityResult = { resultCode: RESULT_CODE, want: { bundleName: 'com.example.uiabilitycomponentsinteractive02', moduleName: 'entry', abilityName: 'EntryAbility', parameters: { info: '来自FuncIndex页面' } } } context.terminateSelfWithResult(abilityResult, err => { if (err.code) { hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self with result. Code is ${err.code}, message is ${err.message}`) return } }) }) } .height('100%') .width('100%') } } |
3.module.json5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
{ "module": { "name": "entry", "type": "entry", "description": "$string:module_desc", "mainElement": "EntryAbility", "deviceTypes": [ "phone", "tablet", "2in1" ], "deliveryWithInstall": true, "installationFree": false, "pages": "$profile:main_pages", "abilities": [ { "name": "EntryAbility", "srcEntry": "./ets/entryability/EntryAbility.ets", "description": "$string:EntryAbility_desc", "icon": "$media:layered_image", "label": "$string:EntryAbility_label", "startWindowIcon": "$media:startIcon", "startWindowBackground": "$color:start_window_background", "exported": true, "skills": [ { "entities": [ "entity.system.home" ], "actions": [ "action.system.home" ] } ] }, { "launchType": "specified", "removeMissionAfterTerminate": true, "name": "FuncAbility", "srcEntry": "./ets/entryability/FuncAbility.ets", "description": "$string:FuncAbility_desc", "icon": "$media:layered_image", "label": "$string:FuncAbility_label", "startWindowIcon": "$media:startIcon", "startWindowBackground": "$color:start_window_background", "exported": true, "skills": [ { "entities": [ "entity.system.home" ], "actions": [ "action.system.home" ] } ] } ], "extensionAbilities": [ { "name": "EntryBackupAbility", "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", "type": "backup", "exported": false, "metadata": [ { "name": "ohos.extension.backup", "resource": "$profile:backup_config" } ], } ] } } |
4.GIF演示
5.下载源码
启动其他应用的UIAbility
启动其他应用的UIAbility,通常用户只需要完成一个通用的操作(例如需要选择一个文档应用来查看某个文档的内容信息),推荐使用隐式Want启动。系统会根据调用方的want参数来识别和启动匹配到的应用UIAbility。
启动UIAbility有显式Want启动和隐式Want启动两种方式。
- 显式Want启动:启动一个确定应用的UIAbility,在want参数中需要设置该应用bundleName和abilityName,当需要拉起某个明确的UIAbility时,通常使用显式Want启动方式。
- 隐式Want启动:根据匹配条件由用户选择启动哪一个UIAbility,即不明确指出要启动哪一个UIAbility(abilityName参数未设置),在调用startAbility()方法时,其入参want中指定了一系列的entities字段(表示目标UIAbility额外的类别信息,如浏览器、视频播放器)和actions字段(表示要执行的通用操作,如查看、分享、应用详情等)等参数信息,然后由系统去分析want,并帮助找到合适的UIAbility来启动。当需要拉起其他应用的UIAbility时,开发者通常不知道用户设备中应用的安装情况,也无法确定目标应用的bundleName和abilityName,通常使用隐式Want启动方式。
1.配置待匹配应用
将多个待匹配的文档应用安装到设备,在其对应UIAbility的module.json5配置文件中,配置skills标签的entities字段和actions字段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
{ "module": { "abilities": [ { ... "skills": [ { "entities": [ ... "entity.system.default" ], "actions": [ ... "ohos.want.action.viewData" ] } ] } ] } } |
2.配置调用方页面
在调用方want参数中的entities和action需要被包含在待匹配UIAbility的skills配置的entities和actions中。系统匹配到符合entities和actions参数条件的UIAbility后,会弹出选择框展示匹配到的UIAbility实例列表供用户选择使用。示例中的context的获取方式请参见获取UIAbility的上下文信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import { common, Want } from '@kit.AbilityKit' import { hilog } from '@kit.PerformanceAnalysisKit' import { BusinessError } from '@kit.BasicServicesKit' const DOMAIN_NUMBER: number = 0xFF00 const TAG: string = '[Page_Interactive03Index]' @Entry @Component struct Index { build() { RelativeContainer() { Button("启动其他应用的UIAbility") .alignRules({ center: { anchor: '__container__', align: VerticalAlign.Center }, middle: { anchor: '__container__', align: HorizontalAlign.Center } }) .onClick(() => { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext let want: Want = { deviceId: '', // deviceId为空表示本设备 // uncomment line below if wish to implicitly query only in the specific bundle. // bundleName: 'com.example.uiabilitycomponentsinteractive031', action: 'ohos.want.action.viewData', // entities can be omitted. entities: ['entity.system.default'] } context.startAbility(want).then(() => { hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting FuncAbility.') }).catch((err: BusinessError) => { hilog.error(DOMAIN_NUMBER, TAG, `Failed to start FuncAbility. Code is ${err.code}, message is ${err.message}`) }) }) } .height('100%') .width('100%') } } |
3.GIF演示
4.下载源码
启动其他应用的UIAbility并获取返回结果
同启动应用内的UIAbility并获取返回结果
启动UIAbility的指定页面
概述
一个UIAbility可以对应多个页面,在不同的场景下启动该UIAbility时需要展示不同的页面,例如从一个UIAbility的页面中跳转到另外一个UIAbility时,希望启动目标UIAbility的指定页面。
UIAbility的启动分为两种情况:UIAbility冷启动和UIAbility热启动。
- UIAbility冷启动:指的是UIAbility实例处于完全关闭状态下被启动,这需要完整地加载和初始化UIAbility实例的代码、资源等。
- UIAbility热启动:指的是UIAbility实例已经启动并在前台运行过,由于某些原因切换到后台,再次启动该UIAbility实例,这种情况下可以快速恢复UIAbility实例的状态。
调用方UIAbility指定启动页面
调用方UIAbility启动另外一个UIAbility时,通常需要跳转到指定的页面。例如FuncAbility包含两个页面(Index对应首页,Second对应功能A页面),此时需要在传入的want参数中配置指定的页面路径信息,可以通过want中的parameters参数增加一个自定义参数传递页面跳转信息。示例中的context的获取方式请参见获取UIAbility的上下文信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
import { common, Want } from '@kit.AbilityKit' import { hilog } from '@kit.PerformanceAnalysisKit' import { BusinessError } from '@kit.BasicServicesKit' const DOMAIN_NUMBER: number = 0xFF00 const TAG: string = '[Page_Interactive04Index]' @Entry @Component struct Index { @State message: string = 'Hello World'; build() { Row() { Column({ space: 10 }) { Text('UIAbility Components Interactive04') Button("调用方UIAbility指定启动页面") .alignRules({ center: { anchor: '__container__', align: VerticalAlign.Center }, middle: { anchor: '__container__', align: HorizontalAlign.Center } }) .onClick(() => { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext let want: Want = { deviceId: '', bundleName: 'com.example.uiabilitycomponentsinteractive04', moduleName: 'entry', abilityName: 'FuncAbility', parameters: { router: 'funcA' } } context.startAbility(want).then(() => { hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting FuncAbility.') }).catch((err: BusinessError) => { hilog.error(DOMAIN_NUMBER, TAG, `Failed to start FuncAbility. Code is ${err.code}, message is ${err.message}`) }) }) } .width('100%') } .height('100%') } } |
目标UIAbility冷启动
目标UIAbility冷启动时,在目标UIAbility的onCreate()生命周期回调中,接收调用方传过来的参数。然后在目标UIAbility的onWindowStageCreate()生命周期回调中,解析EntryAbility传递过来的want参数,获取到需要加载的页面信息url,传入windowStage.loadContent()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { window } from '@kit.ArkUI'; export default class FuncAbility extends UIAbility { funcAbilityWant: Want | undefined = undefined; onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { this.funcAbilityWant = want } onDestroy(): void { } onWindowStageCreate(windowStage: window.WindowStage): void { let url = 'pages/Index'; if (this.funcAbilityWant?.parameters?.router && this.funcAbilityWant.parameters.router === 'funcA') { url = 'pages/Page_ColdStartUp'; } windowStage.loadContent(url, (err) => { if (err.code) { hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); return; } hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); }); } } |
目标UIAbility热启动
在应用开发中,会遇到目标UIAbility实例之前已经启动过的场景,这时再次启动目标UIAbility时,不会重新走初始化逻辑,只会直接触发onNewWant()生命周期方法。为了实现跳转到指定页面,需要在onNewWant()中解析参数进行处理。
- 冷启动短信应用的UIAbility实例时,在onWindowStageCreate()生命周期回调中,通过调用getUIContext()接口获取UI上下文实例UIContext对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
import { hilog } from '@kit.PerformanceAnalysisKit'; import { Want, UIAbility } from '@kit.AbilityKit'; import { window, UIContext } from '@kit.ArkUI'; const DOMAIN_NUMBER: number = 0xFF00; const TAG: string = '[EntryAbility]'; export default class EntryAbility extends UIAbility { funcAbilityWant: Want | undefined = undefined; uiContext: UIContext | undefined = undefined; // ... onWindowStageCreate(windowStage: window.WindowStage): void { // Main window is created, set main page for this ability hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onWindowStageCreate'); let url = 'pages/Index'; if (this.funcAbilityWant?.parameters?.router && this.funcAbilityWant.parameters.router === 'funcA') { url = 'pages/Page_ColdStartUp'; } windowStage.loadContent(url, (err, data) => { if (err.code) { return; } let windowClass: window.Window; windowStage.getMainWindow((err, data) => { if (err.code) { hilog.error(DOMAIN_NUMBER, TAG, `Failed to obtain the main window. Code is ${err.code}, message is ${err.message}`); return; } windowClass = data; this.uiContext = windowClass.getUIContext(); }); hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); }); } } |
在短信应用UIAbility的onNewWant()回调中解析调用方传递过来的want参数,通过调用UIContext中的getRouter()方法获取Router对象,并进行指定页面的跳转。此时再次启动该短信应用的UIAbility实例时,即可跳转到该短信应用的UIAbility实例的指定页面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import { AbilityConstant, Want, UIAbility } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import type { Router, UIContext } from '@kit.ArkUI'; import type { BusinessError } from '@kit.BasicServicesKit'; const DOMAIN_NUMBER: number = 0xFF00; const TAG: string = '[EntryAbility]'; export default class EntryAbility extends UIAbility { funcAbilityWant: Want | undefined = undefined; uiContext: UIContext | undefined = undefined; onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void { if (want?.parameters?.router && want.parameters.router === 'funcA') { let funcAUrl = 'pages/Page_HotStartUp'; if (this.uiContext) { let router: Router = this.uiContext.getRouter(); router.pushUrl({ url: funcAUrl }).catch((err: BusinessError) => { hilog.error(DOMAIN_NUMBER, TAG, `Failed to push url. Code is ${err.code}, message is ${err.message}`); }); } } } // ... } |