UIAbility组件
UIAbility组件是一种包含UI界面的应用组件,主要用于和用户交互。直白来说就是构建页面,可以通过多个页面来实现功能模块。

创建的module默认情况下就是一个ability,除此之外还有HAR(静态资源包)和HSP(动态共享包),主要用于module间共用资源,后续会做详细讲解。
主要内容:
1. `ability module`目录结构及声明配置;
2. 生命周期;
3. 与UI界面数据同步;
4. 应用内`UIAbility`间跳转;
5. 启动模式;
一、ability module目录结构及声明配置

资源访问
如`media`中的`icon.png`,可以这样访问:
```js
//页面中
Image($r("app.media.icon"))
// 配置文件中
"icon": "$media:icon"
```
国际化使用(文本):
```js
// element/string.json 默认文本
{
"string": [
{
"name": "title",
"value": "柏木白"
}
]
}
// en_US/element/string.json 英文
{
"string": [
{
"name": "title",
"value": "1000phone"
}
]
}
// zh_CN/element/string.json 中文
{
"string": [
{
"name": "title",
"value": "柏木白"
}
]
}
//页面中访问
Text($r("app.string.title"))
// 配置文件中
"label": "$string:title"
```
声明配置
为使应用能够正常使用`UIAbility`,需要在`module.json5`配置文件的`abilities`标签中声明`UIAbility`的名称、入口、标签等相关信息。
```js
{
"module": {
// ...
"abilities": [
{
"name": "EntryAbility", // UIAbility组件的名称
"srcEntrance": "./ets/entryability/EntryAbility.ts", // 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组件启动页面背景颜色资源文件的索引
// ...
}
]
}
}
```
二、生命周期
当用户打开、切换和返回到对应应用时,应用中的`UIAbility`实例会在其生命周期的不同状态之间转换。
`UIAbility`的生命周期包括`Create`、`Foreground`、`Background`、`Destroy`四个状态,如下图所示。

`Create`状态为在应用加载过程中,`UIAbility`实例创建完成时触发,系统会调用`onCreate()`回调。可以在该回调中进行应用初始化操作,例如变量定义资源加载等,用于后续的UI界面展示。
```js
import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
// 应用初始化
// want中包含如包代码路径、Bundle名称、Ability名称和应用程序需要的环境状态等属性信息
// launchParam包含启动参数 主要在被其他UIAbility唤起时使用
// 可通过this.context访问上下文对象 后续会做讲解
}
// ...
}
```
`UIAbility`实例创建完成之后,在进入`Foreground`之前,系统会创建一个`WindowStage`。`WindowStage`创建完成后会进入`onWindowStageCreate()`回调,可以在该回调中设置UI界面加载、设置`WindowStage`的事件订阅。

在`onWindowStageCreate()`回调中通过`loadContent()`方法设置应用要加载的页面并根据需要订阅WindowStage的事件(获焦/失焦、可见/不可见)。
```js
import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: Window.WindowStage) {
// 设置WindowStage的事件订阅(获焦/失焦、可见/不可见)
// 设置UI界面加载
windowStage.loadContent('pages/Index', (err, data) => {
// ...
});
}
}
```
对应于`onWindowStageCreate()`回调。在`UIAbility`实例销毁之前,则会先进入`onWindowStageDestroy()`回调,可以在该回调中释放UI界面资源。例如在`onWindowStageDestroy()`中注销获焦/失焦等`WindowStage`事件。
```js
import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onWindowStageDestroy() {
// 释放UI界面资源
}
}
```
`Foreground`和`Background`状态分别在`UIAbility`实例切换至前台和切换至后台时触发,对应于`onForeground()`回调和`onBackground()`回调。
- `onForeground()`回调,在`UIAbility`的UI界面可见之前,如`UIAbility`切换至前台时触发。可以在`onForeground()`回调中申请系统需要的资源,或者重新申请在`onBackground()`中释放的资源。
- `onBackground()`回调,在`UIAbility`的UI界面完全不可见之后,如`UIAbility`切换至后台时候触发。可以在`onBackground()`回调中释放UI界面不可见时无用的资源,或者在此回调中执行较为耗时的操作,例如状态保存等。
```js
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onForeground() {
// 申请系统需要的资源,或者重新申请在onBackground中释放的资源
}
onBackground() {
// 释放UI界面不可见时无用的资源,或者在此回调中执行较为耗时的操作
// 例如状态保存等
}
}
```
`Destroy`状态在`UIAbility`实例销毁时触发。可以在`onDestroy()`回调中进行系统资源的释放、数据的保存等操作。
```js
import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onDestroy() {
// 系统资源的释放、数据的保存等
}
}
```
三、与UI界面同步
> 有时我们需要让`UIAbility`组件与UI界面之间进行通信,例如:通知UI界面应用的前后台切换、多`module`下的UI界面传值、传递启动参数等。
基于HarmonyOS的应用模型,可以通过以下两种方式来实现:
- `EventHub`:基于发布订阅模式来实现,事件需要先订阅后发布,订阅者收到消息后进行处理。
- `globalThis`:`ArkTS`引擎实例内部的一个全局对象,在`ArkTS`引擎实例内部都能访问。
- 使用`AppStorage`/`LocalStorage`进行数据同步:`ArkUI`提供了`AppStorage`和`LocalStorage`两种应用级别的状态管理方案,可用于实现应用级别和`UIAbility`级别的数据同步。
EventHub
主要使用上下文对象中的`eventHub`模块,提供了事件中心,提供订阅、取消订阅、触发事件的能力。
```js
//UIAbility组件中访问上下文
export default class EntryAbility extends UIAbility {
onCreate() {
// 创建之后 可通过this.context访问上下文对象
}
}
// UI页面访问上下文对象
import common from '@ohos.app.ability.common';
@Entry
@Component
struct Index {
private context = getContext(this) as common.UIAbilityContext;
}
```
通信示例
```js
//UIAbility组件---------------------------
export default class EntryAbility extends UIAbility {
onCreate(){
this.context.eventHub.on("UIEmit",(data)=>{
//通知其他Ability
})
}
onForeground() {
// 触发自定义事件 第一个参数:事件名 后续参数作为监听回调的参数
// 切至前台 通知页面启动计时器
this.context.eventHub.emit("EventName","start")
}
onBackground() {
// 切至后台 通知页面停止计时器
this.context.eventHub.emit("EventName","stop")
}
}
// UI界面--------------------------------
import common from '@ohos.app.ability.common';
@Entry
@Component
struct Index {
private context = getContext(this) as common.UIAbilityContext;
@State num:number=0;
private timer;
aboutToAppear(){
this.start();
//监听前后台切换事件
this.context.eventHub.on("EventName",(data)=>{
this[data]()
})
}
start(){
this.timer=setInterval(()=>{
this.num++;
},1000)
}
stop(){
clearInterval(this.timer);
}
build() {
Column(){
Text(this.num.toString())
.width("100%")
.height("100%")
.fontSize(30)
.textAlign(TextAlign.Center)
Button(){
Text("页面触发事件")
}
.onClick(()=>{
// 向UIAbility发射信号
this.context.eventHub.emit("UIEmit","params");
})
}
}
}
```
globalThis
`globalThis`是`ArkTS`引擎实例内部的一个全局对象,引擎内部的`UIAbility/ExtensionAbility/Page`都可以使用,因此可以使用`globalThis`对象进行数据同步。

```js
//UIAbility组件---------------------------
export default class EntryAbility extends UIAbility {
onCreate(want, launch) {
globalThis.title="柏木白";
}
}
// UI界面--------------------------------
@Entry
@Component
struct Index {
aboutToAppear() {
console.log(globalThis)
}
}
```
<p style="color:red">globalThis注意事项</p>

- `Stage`模型下进程内的`UIAbility`组件共享`ArkTS`引擎实例,使用`globalThis`时需要避免存放相同名称的对象。例如`AbilityA`和`AbilityB`可以使用`globalThis`共享数据,在存放相同名称的对象时,先存放的对象会被后存放的对象覆盖。
- `FA`模型因为每个`UIAbility`组件之间引擎隔离,不会存在该问题。
- 对于绑定在`globalThis上`的对象,其生命周期与`ArkTS`虚拟机实例相同,建议在使用完成之后将其赋值为`null`,以减少对应用内存的占用。
AppStorage/LocalStorage
`ArkUI`提供了`AppStorage`和`LocalStorage`两种应用级别的状态管理方案,可用于实现应用级别和UIAbility级别的数据同步。其中,`AppStorage`是一个全局的状态管理器,适用于多个`UIAbility`共享同一状态数据的情况;而`LocalStorage`则是一个局部的状态管理器,适用于单个`UIAbility`内部使用的状态数据。
LocalStorage
UIAbility组件中创建LocalStorage实例:
```js
export default class EntryAbility extends UIAbility {
// 创建LocalStorage实例
storage: LocalStorage = new LocalStorage({
title:"柏木白"
})
onWindowStageCreate(windowStage: window.WindowStage) {
// 注入页面
windowStage.loadContent('pages/Index',this.storage)
}
}
```
在UI页面通过GetShared接口获取在通过loadContent共享的LocalStorage实例:
```js
// 通过GetShared接口获取stage共享的LocalStorage实例
let storage = LocalStorage.GetShared()
@Entry(storage)
@Component
struct CompA {
//组件内部可以用@LocalStorageLink/Prop接收
@LocalStorageLink('title') title: string="";
build() {
Column() {
Text(this.title).fontSize(50)
}
}
}
```
<span style="color:red">---- 注意:存储在预览视图中无效 需要用模拟器或真机调试 -----</span>
AppStorage
`AppStorage`是应用级的全局状态共享,相当于整个应用的“中枢”,是在应用启动的时候会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。
随时随地设置:
```js
export default class EntryAbility extends UIAbility {
onCreate(){
AppStorage.SetOrCreate("name","周杰伦")
}
}
```
在UI页面通过`@StorageLink`访问:
```js
@Entry
@Component
struct Storage {
@StorageLink('name') name: string = "";
build() {
Row() {
Column() {
Text(this.name)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}
```
四、应用内UIAbility间跳转
> `UIAbility`组件是系统调度的基本单元,为应用提供绘制界面的窗口;一个`UIAbility`组件中可以通过多个页面来实现一个功能模块。每一个`UIAbility`组件实例,都对应于一个最近任务列表中的任务。
对于开发者而言,可以根据具体场景选择单个还是多个`UIAbility`,划分建议如下:
* 如果希望在任务视图中看到一个任务,则建议使用一个`UIAbility`,多个页面的方式。
* 如果希望在任务视图中看到多个任务,或者需要同时开启多个窗口,则建议使用多个`UIAbility`开发不同的模块功能。
* `Entry module`是项目主模块,当应用比较大时,可以拆分`Feature module`,配置成按需下载安装,不同设备下也可以随意搭配组合。
1.新建`module`

得到一个和`entry`一样的目录结构:

`module.json5`文件中包含模块的基本信息:

2.在`entry`的页面中编写跳转逻辑:
```js
import common from '@ohos.app.ability.common';
@Entry
@Component
struct Index {
// 获取上下文对象
private context = getContext(this) as common.UIAbilityContext;
build() {
Navigation(){
Flex({justifyContent:FlexAlign.Center,alignItems:ItemAlign.Center}){
Button({type:ButtonType.Capsule}){
Text("跳转").fontColor("white")
}
.padding(15)
.width(150)
.onClick(()=>{
//跳转至指定Ability
this.context.startAbility({
deviceId:"",//设备id 留空表示当前设备
bundleName:"com.example.myapplication",//包名 可以从AppScope/app.json5中拿到
moduleName:"application",//模块名
abilityName:"ApplicationAbility",
parameters:{//携带的参数 可以借助参数指定目标Ability启动的页面
val:"柏木白"
}
})
})
}
.width("100%")
.height("100%")
}
.title("入口模块首页")
}
}
```
跳转目标接收参数 在页面中显示:
```js
//UIAbility组件 ------------------------------------------
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
export default class ApplicationAbility extends UIAbility {
onCreate(want, launchParam) {
// 接收参数
globalThis.title=want.parameters.title;
}
onWindowStageCreate(windowStage: window.WindowStage) {
windowStage.loadContent('pages/Index');
}
}
//UI界面-------------------------------------------------
@Entry
@Component
struct Index {
build() {
Row() {
Column() {
Text("接收到的数据:"+globalThis.title)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}
```
3.预览调试
编辑部署配置:


选择部署多个hap包:

运行效果:



4.返回并携带参数
目标Ability中的页面,调用terminateSelfWithResult()方法,入参为需要返回给EntryAbility的信息:
```js
import common from '@ohos.app.ability.common';
@Entry
@Component
struct Index {
// 获取上下文对象
private context = getContext(this) as common.UIAbilityContext;
build() {
Row() {
Column() {
Text("接收到的数据:"+globalThis.title)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Button({type:ButtonType.Capsule}){
Text("返回").fontColor("white")
}
.padding(15)
.margin(10)
.width(150)
.onClick(()=>{
let abilityResult = {
resultCode: 200,
want: {
parameters: {
title:"鸿蒙生态"
},
},
}
//停止自身 并携带数据返回
this.context.terminateSelfWithResult(abilityResult);
})
}
.width('100%')
}
.height('100%')
}
}
```
调用方跳转方法改为`startAbilityForResult`,通过回调访问结果:
```js
//跳转至指定Ability并要接收结果
this.context.startAbilityForResult({
deviceId:"",//设备id 留空表示当前设备
bundleName:"com.example.myapplication",//包名 可以从AppScope/app.json5中拿到
moduleName:"application",//模块名
abilityName:"ApplicationAbility",
parameters:{
title:"柏木白"
}
})
.then((data:any) => {
//接收目标销毁时返回的数据
console.log(data.want.parameters.title,"-----------------");
})
```
5.目标`UIAbility`是否首次启动
目标`UIAbility`首次启动时,在目标`UIAbility`的`onWindowStageCreate()`生命周期回调中,解析`EntryAbility`传递过来的want参数,获取到需要加载的页面信息`url`,传入`windowStage.loadContent()`方法。
```js
onWindowStageCreate(windowStage: Window.WindowStage) {
let url =this.context.want.parameters.url||'pages/Index';
windowStage.loadContent(url);
}
```
目标`UIAbility`非首次启动,在目标`UIAbility`中,默认加载的是`Index`页面。由于当前`UIAbility`实例之前已经创建完成,此时会进入`UIAbility`的`onNewWant()`回调中且不会进入`onCreate()`和`onWindowStageCreate()`生命周期回调,在`onNewWant()`回调中解析调用方传递过来的`want`参数,并挂在到全局变量`globalThis`中,以便于后续在页面中获取。
```js
//增加onNewWant钩子
onNewWant(want, launchParam) {
// 接收调用方UIAbility传过来的参数
globalThis.url = want.parameters.url;
}
```
目标`UIAbility`首页中判断跳转:
```js
import router from '@ohos.router';
@Entry
@Component
struct Index {
onPageShow() {
if (globalThis.url) {
router.replaceUrl({
url: globalThis.url,
})
}
}
}
```
五、启动模式
`UIAbility`的启动模式是指`UIAbility`实例在启动时的不同呈现状态。针对不同的业务场景,系统提供了三种启动模式:
- singleton(单实例模式)
- multiton(多实例模式)
- specified(指定实例模式)
1. `singleton`启动模式
`singleton`启动模式为单实例模式,也是默认情况下的启动模式。每次调用`startAbility()`方法时,如果应用进程中该类型的`UIAbility`实例已经存在,则复用系统中的`UIAbility`实例。系统中只存在唯一一个该`UIAbility`实例,即在最近任务列表中只存在一个该类型的`UIAbility`实例。
> 注意:应用的`UIAbility`实例已创建,该`UIAbility`配置为单实例模式,再次调用`startAbility()`方法启动该`UIAbility`实例。由于启动的还是原来的`UIAbility`实例,并未重新创建一个新
> `UIAbility`实例,此时只会进入该`UIAbility`的`onNewWant()`回调,不会进入其`onCreate()`和`onWindowStageCreate()`生命周期回调。
如果需要使用`singleton`启动模式,在module.json5配置文件中的`launchType`字段配置为`singleton`即可:
```js
{
"module": {
...
"abilities": [
{
"launchType": "singleton",
...
}
]
}
}
```
2. `multiton`启动模式
`multiton`启动模式为多实例模式,每次调用`startAbility()`方法时,都会在应用进程中创建一个新的该类型`UIAbility`实例。即在最近任务列表中可以看到有多个该类型的`UIAbility`实例。这种情况下可以将`UIAbility`配置为`multiton`(多实例模式)。
`multiton`启动模式的开发使用,在`module.json5`配置文件中的`launchType`字段配置为`multiton`即可。
```js
{
"module": {
...
"abilities": [
{
"launchType": "multiton",
...
}
]
}
}
```
3. `specified`启动模式
`specified`启动模式为指定实例模式,针对一些特殊场景使用(例如文档应用中每次新建文档希望都能新建一个文档实例,重复打开一个已保存的文档希望打开的都是同一个文档实例)。
`specified`启动模式的开发使用,在`module.json5`配置文件中的`launchType`字段配置为`specified`即可。
```js
{
"module": {
...
"abilities": [
{
"launchType": "specified",
...
}
]
}
}
```
在`EntryAbility`中,调用`startAbility()`方法时,在`want`参数中,增加一个自定义参数来区别`UIAbility`实例,例如增加一个"instanceKey"自定义参数。
```js
// 在启动指定实例模式的UIAbility时,给每一个UIAbility实例配置一个独立的Key标识
// 例如在文档使用场景中,可以用文档路径作为Key标识
function getInstance() {
// return id
}
let want = {
deviceId: '', // deviceId为空表示本设备
bundleName: 'com.example.myapplication',
abilityName: 'FuncAbility',
moduleName: 'module1', // moduleName非必选
parameters: { // 自定义信息
instanceKey: getInstance(),
},
}
// context为调用方UIAbility的AbilityContext
this.context.startAbility(want).then(() => {
// ...
}).catch((err) => {
// ...
})
```
目标`UIAbility`:
```js
import AbilityStage from '@ohos.app.ability.AbilityStage';
export default class MyAbilityStage extends AbilityStage {
onAcceptWant(want): string {
// 在被调用方的AbilityStage中,针对启动模式为specified的UIAbility返回一个UIAbility实例对应的一个Key值
return want.parameters.instanceKey
}
}
```
1890

被折叠的 条评论
为什么被折叠?



