UIAbility组件
组件概述
概述
UIAbility是一种包含UI的应用组件,是系统调用的基本单元,主要用于和用户交互。
声明配置
在module.json5配置文件的abilities标签中声明UIAbility的配置项
{
"module": {
...
"abilities": [
{
"name": "EntryAbility", // UIAbility组件的名称
"srcEntry": "./ets/entryability/EntryAbility.ets", // UIAbility组件的代码路径
"description": "$string:EntryAbility_desc", // UIAbility组件的描述信息
"icon": "$media:icon", // UIAbility组件的图标
"label": "$string:EntryAbility_label", // UIAbility组件的标签
"startWindowIcon": "$media:icon", // UIAbility组件启动页面图标资源文件的索引
"startWindowBackground": "$color:start_window_background", // UIAbility组件启动页面背景颜色资源文件的索引
...
}
]
}
}
生命周期
-
Create:在应用加载过程中,UIAbility实例创建完成时触发,系统会调用**onCreate()**回调。
可以在该回调中进行页面初始化操作,例如变量定义资源加载等,用于后续的UI展示。
-
Foreground:UIAbility实例切换至前台触发,对应**onForeground()**回调。
可以申请系统需要的资源,或者重新申请在onBackground()中释放的资源。
-
Background:UIAbility实例切换至后台触发,对应**onForeground()**回调。
可以释放UI不可见时无用的资源,或者在此回调中执行较为耗时的操作,例如状态保存等。
-
Destroy:UIAbility实例销毁时触发,onDestroy()回调。
可以进行系统资源的释放、数据的保存等操作。
note:WindowStageCreate和WindowStageDestroy状态
WindowStageCreate
UIAbility实例创建完成之后,在进入Foreground之前,系统会创建一个WindowStage。WindowStage创建完成后会进入**onWindowStageCreate()**回调,可以在该回调中设置UI加载、设置WindowStage的事件订阅。
WindowStageDestroy
在UIAbility实例销毁之前,则会先进入onWindowStageDestroy()回调,可以在该回调中释放UI资源。
启动模式
在module.json5配置文件中配置launchType字段
singleton(单实例模式)(默认)
- 每次调用startAbility()方法时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例。系统中只存在唯一一个该UIAbility实例,即在最近任务列表中只存在一个该类型的UIAbility实例。
multiton(多实例模式)
- multiton启动模式为多实例模式,每次调用startAbility()方法时,都会在应用进程中创建一个新的该类型UIAbility实例。即在最近任务列表中可以看到有多个该类型的UIAbility实例。这种情况下可以将UIAbility配置为multiton(多实例模式)。
specified(指定实例模式)
- specified启动模式为指定实例模式,针对一些特殊场景使用(例如文档应用中每次新建文档希望都能新建一个文档实例,重复打开一个已保存的文档希望打开的都是同一个文档实例)。
eg:有两个UIAbility:A_Ability和B_Ability,需要从A_Ability的页面中启动B_Ability,B_Ability配置为指定实例模式。
步骤:
module.json5的launchType字段配置为specified。
在创建B_UIAbility实例前,可以为该实例指定一个唯一的字符串Key,这样在调用startAbility()方法时,应用就可以根据指定的Key来识别响应请求的UIAbility实例。
在A_Ability中,调用startAbility()方法时,可以在want参数中增加一个自定义参数,例如instanceKey,以此来区分不同的UIAbility实例。
// 在启动指定实例模式的UIAbility时,给每一个UIAbility实例配置一个独立的Key标识 // 例如在文档使用场景中,可以用文档路径作为Key标识 import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; import Want from '@ohos.app.ability.Want'; @Entry @Component struct Page_StartModel { private KEY_NEW = 'KEY'; build() { Row() { Column() { Button() .onClick(() => { let context:common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // context为调用方UIAbility的UIAbilityContext; let want: Want = { deviceId: '', // deviceId为空表示本设备 bundleName: 'com.samples.stagemodelabilitydevelop', abilityName: 'B_Ability', moduleName: 'entry', // moduleName非必选 parameters: { // 自定义信息 instanceKey: this.KEY_NEW } }; context.startAbility(want).then(() => { console.log('Succeeded in starting B_Ability.'); }).catch((err) => { console.error(`Failed to start B_Ability.`); }) this.KEY_NEW = this.KEY_NEW + 'a'; }) } .width('100%') } .height('100%') } }
指定实例模式的B_Ability启动之前,会先进入对应的AbilityStage的startAbility()生命周期回调中,以获取该UIAbility实例的Key值。然后系统对Key匹配,若之前有相同的UIAbility会进入该UIAbility实例的onNewWant()回调函数,否则会创建一个新的UIAbility实例
import AbilityStage from '@ohos.app.ability.AbilityStage'; import type Want from '@ohos.app.ability.Want'; export default class MyAbilityStage extends AbilityStage { onAcceptWant(want: Want): string { // 在被调用方的AbilityStage中,针对启动模式为specified的UIAbility返回一个UIAbility实例对应的一个Key值 // 当前示例指的是B_Ability实例 if (want.abilityName === 'B_Ability') { // 返回的字符串Key标识为自定义拼接的字符串内容 if (want.parameters) { return `${want.parameters.instanceKey}`; } } return ''; } }
基本用法
指定UIAbility的启动页面
在UIAbility的onWindowStageCreate()生命周期回调中,通过WindowStage对象的loadContent()方法设置启动页面。
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
windowStage.loadContent('pages/Index', (err, data) => {
});
}
}
获取UIAbility的上下文信息
-
在UIAbility中可以通过this.context获取UIAbility实例的上下文信息。
export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { // 获取UIAbility实例的上下文 let context = this.context; // ... } }
-
通过getContext()在页面中获取UIAbility实例的上下文信息。
import common from '@ohos.app.ability.common'; import Want from '@ohos.app.ability.Want'; @Entry @Component struct Index { private context = getContext(this) as common.UIAbilityContext; startAbilityTest() { let want: Want = { // Want参数信息 }; this.context.startAbility(want); } build() { // ... } }
UIAbility组件与UI的数据同步
EventHub进行数据通信
在基类Context中,提供了EventHub对象,EventHub为UIAbility组件提供了事件机制,使它们能够进行订阅、取消订阅和触发事件等数据通信能力。
-
在UIAbility中调用EventHub.on()方法注册一个自定义事件“event1”。
import hilog from '@ohos.hilog'; import UIAbility from '@ohos.app.ability.UIAbility'; import type window from '@ohos.window'; import type { Context } from '@ohos.abilityAccessCtrl'; import Want from '@ohos.app.ability.Want' import type AbilityConstant from '@ohos.app.ability.AbilityConstant'; const DOMAIN_NUMBER: number = 0xFF00; const TAG: string = '[EventAbility]'; export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { // 获取UIAbility实例的上下文 let context = this.context; // 获取eventHub let eventhub = this.context.eventHub; // 执行订阅操作 eventhub.on('event1', this.eventFunc); eventhub.on('event1', (data: string) => { // 触发事件,完成相应的业务操作 }); } eventFunc(argOne: Context, argTwo: Context): void { hilog.info(DOMAIN_NUMBER, TAG, '1. ' + `${argOne}, ${argTwo}`); return; } }
-
在UI中通过EventHub.emit()方法触发该事件,在触发事件的同时,根据需要传入参数信息。
import common from '@ohos.app.ability.common'; import promptAction from '@ohos.promptAction' @Entry @Component struct Page_EventHub { private context = getContext(this) as common.UIAbilityContext; eventHubFunc() : void { // 不带参数触发自定义“event1”事件 this.context.eventHub.emit('event1'); // 带1个参数触发自定义“event1”事件 this.context.eventHub.emit('event1', 1); // 带2个参数触发自定义“event1”事件 this.context.eventHub.emit('event1', 2, 'test'); // 开发者可以根据实际的业务场景设计事件传递的参数 } build() { Column() { Text() .onClick(() => { this.eventHubFunc(); promptAction.showToast({ message: 'emit' }); }) Text() .onClick(() => { this.context.eventHub.off('event1'); promptAction.showToast({ message: 'off' }); }) } .width('100%') .margin({ top: 8 }) } }
-
在UIAbility的注册事件回调中可以得到对应的触发事件结果,运行日志结果如下所示。
[Example].[Entry].[EntryAbility] 1. [] [Example].[Entry].[EntryAbility] 1. [1] [Example].[Entry].[EntryAbility] 1. [2,"test"]
-
在自定义事件“event1”使用完成后,可以根据需要调用EventHub.off()方法取消该事件的订阅。
// context为UIAbility实例的AbilityContext this.context.eventHub.off('event1');
AppStorage/LocalStorage通信
UIAbility组件间交互
启动本应用UIAbility
假设应用中有两个UIAbility:A_Ability和B_Ability(可以在同一个Module中,也可以在不同的Module中),需要从A_Ability的页面中启动B_Ability。
-
在A_Ability中,通过调用startAbility()方法启动UIAbility,want为UIAbility实例启动的入口参数,parameters为自定义信息参数。
import common from '@ohos.app.ability.common'; import Want from '@ohos.app.ability.Want'; @Entry @Component struct A_UIAbilityPage { private context = getContext(this) as common.UIAbilityContext; build() { Button() .onClick(() => { let wantInfo: Want = { deviceId: '', // deviceId为空表示本设备 bundleName: 'com.samples.myapplication', moduleName: 'entry', // moduleName非必选 abilityName: 'B_Ability', parameters: { // 自定义信息 info: '来自A_Ability的A_UIAbilityPage页面' }, } // context为调用方UIAbility的UIAbilityContext this.context.startAbility(wantInfo).then(……).catch(……); }) } }
-
在B_Ability的onCreat()或者onNewWant()生命周期回调文件中接收A_Ability传递过来的参数。
import UIAbility from '@ohos.app.ability.UIAbility'; import AbilityConstant from '@ohos.app.ability.AbilityConstant'; import Want from '@ohos.app.ability.Want'; export default class B_Ability extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { // 接收调用方UIAbility传过来的参数 let B_AbilityWant = want; let info = B_AbilityWant?.parameters?.info; // ... } }
-
在B_Ability业务完成之后,如需要停止当前UIAbility实例,在B_Ability中通过调用terminateSelf()方法实现。
调用terminateSelf()方法停止当前UIAbility实例时,默认会保留该实例的快照(Snapshot),即在最近任务列表中仍然能查看到该实例对应的任务。如不需要保留该实例的快照,可以在其对应UIAbility的module.json5配置文件中,将abilities标签的removeMissionAfterTerminate字段配置为true。
import common from '@ohos.app.ability.common'; @Entry @Component struct B_UIAbilityPage { build() { ... Button() .onClick(() => { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext // context为需要停止的UIAbility实例的AbilityContext context.terminateSelf((err) => { if (err.code) { …… return; } }); }) } }
-
如需要关闭应用所有的UIAbility实例,可以调用ApplicationContext的killAllProcesses()方法实现关闭应用所有的进程。
-
若要在A_Ability获取B_Ability返回的信息
-
在B_Ability停止自身时,需要调用terminateSelfWithResult()方法,入参abilityResult为B_Ability需要返回给A_Ability的信息。
import common from '@ohos.app.ability.common'; @Entry @Component struct B_UIAbilityPage { build() { Button() .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.samples.stagemodelabilitydevelop', moduleName: 'entry', // moduleName非必选 abilityName: 'B_Ability', parameters: { info: '来自B_Ability的B_UIAbilityPage页面' }, }, }; context.terminateSelfWithResult(abilityResult, (err) => { if (err.code) { ... return; } }); }) } }
-
B_Ability停止自身后,A_Ability通过startAbilityForResult()方法回调接收被B_Ability返回的信息,RESULT_CODE需要与前面的数值保持一致
Button() .onClick(() => { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext const RESULT_CODE: number = 1001; let want: Want = { deviceId: '', // deviceId为空表示本设备 bundleName: 'com.samples.stagemodelabilitydevelop', moduleName: 'entry', // moduleName非必选 abilityName: 'B_Ability', parameters: { // 自定义信息 info: '来自A_Ability的A_UIAbilityPage页面' } }; context.startAbilityForResult(want).then((data) => { if (data?.resultCode === RESULT_CODE) { // 解析被调用方UIAbility返回的信息 let info = data.want?.parameters?.info; if (info !== null) { promptAction.showToast({ message: JSON.stringify(info) }); } } }).catch((err: BusinessError) => { ... }); })
-
启动其他应用UIAbility
启动UIAbility有显示Want启动和隐式Want启动两种方式。而此时需要隐式启动Want。
例如从应用A的UIAbility跳到到应用B或C或等等应用的UIAbility
-
将B、C等应用安装到设备,在其对应UIAbility的module.json5配置文件中,配置skills标签的entities字段和actions字段。
{ "module": { "abilities": [ { ... "skills": [ { "entities": [ ... "entity.system.default" ], "actions": [ ... "ohos.want.action.viewData" ] } ] } ] } }
-
在应用A的want参数中的entities和action需要被包含在待匹配UIAbility的skills配置的entities和actions中。系统匹配到符合entities和actions参数条件的UIAbility后,会弹出选择框展示匹配到的UIAbility实例列表供用户选择使用。
Button() .onClick(() => { let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext let want: Want = { deviceId: '', action: 'ohos.want.action.viewData', entities: ['entity.system.default'] }; // context为调用方UIAbility的UIAbilityContext context.startAbility(want).then(() => { ... }).catch((err: BusinessError) => { ... }); })
-
在文档应用使用完成之后,如需要停止当前UIAbility实例,通过调用terminateSelf()方法实现。
-
若要在应用A的UIAbility获取B、C的UIAbility返回的信息
方法与之前近乎相同。
-
在B、C应用的UIAbility返回时,需要调用terminateSelfWithResult()方法实现停止自身,并将abilityResult参数信息返回给调用方。
-
应用A需要使用startAbilityForResult()方法启动支付应用的UIAbility,RESULT_CODE需要与前面terminateSelfWithResult()返回的数值保持一致。异步回调中的data用于后续接收支付UIAbility停止自身后返回给调用方的信息。系统匹配到符合entities和actions参数条件的UIAbility后,会弹出选择框展示匹配到的UIAbility实例列表供用户选择使用。
-
启动UIAbility的指定页面
调用方UIAbility指定启动页面
此时需要在传入的want参数中配置指定的页面路径信息,可以通过want中的parameters参数增加一个自定义参数传递页面跳转信息。
let want: Want = {
deviceId: '', // deviceId为空表示本设备
bundleName: 'com.samples.stagemodelabilityinteraction',
moduleName: 'entry', // moduleName非必选
abilityName: 'FuncAbility',
parameters: { // 自定义参数传递页面信息
router: 'FuncA'
}
}
目标UIAbility冷启动
-
指的是UIAbility实例处于完全关闭状态下被启动,这需要完整地加载和初始化UIAbility实例的代码、资源等。
-
目标UIAbility冷启动时,在目标UIAbility的onCreate()生命周期回调中,接收调用方传过来的参数。然后在目标UIAbility的onWindowStageCreate()生命周期回调中,解析调用方传递过来的want参数,获取到需要加载的页面信息url,传入windowStage.loadContent()方法。
export default class FuncAbility extends UIAbility { funcAbilityWant: Want | undefined = undefined; onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { // 接收调用方UIAbility传过来的参数 this.funcAbilityWant = want; } onWindowStageCreate(windowStage: window.WindowStage) { // Main window is created, set main page for this ability let url = 'pages/Index'; if (this.funcAbilityWant?.parameters?.router && this.funcAbilityWant.parameters.router === 'funcA') { url = 'pages/Page_ColdStartUp'; } windowStage.loadContent(url, (err, data) => { // ... }); } }
目标UIAbility热启动
- 指的是UIAbility实例已经启动并在前台运行过,由于某些原因切换到后台,再次启动该UIAbility实例,这种情况下不会重新走初始化逻辑,只会直接触发onNewWant()生命周期方法。
- 为了实现跳转到指定页面,需要在onNewWant()中解析参数进行处理。
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (want?.parameters?.router && want.parameters.router === 'funcB') {
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}`);
});
}
}
}