当前位置:   article > 正文

HarmonyOS 鸿蒙学习笔记3-UIAbility组件_鸿蒙。前后台切换监听

鸿蒙。前后台切换监听

UIAbility组件

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

创建的module默认情况下就是一个ability,除此之外还有HAR(静态资源包)和HSP(动态共享包),主要用于module间共用资源,后续会做详细讲解。

主要内容:

1. `ability module`目录结构及声明配置;

2. 生命周期;

3. 与UI界面数据同步;

4. 应用内`UIAbility`间跳转;

5. 启动模式;

一、ability module目录结构及声明配置

资源访问

如`media`中的`icon.png`,可以这样访问:

```js

  1. //页面中
  2. Image($r("app.media.icon"))
  3. // 配置文件中
  4. "icon": "$media:icon"

```

国际化使用(文本):

```js

  1. // element/string.json 默认文本
  2. {
  3.   "string": [
  4.     {
  5.       "name": "title",
  6.       "value": "柏木白"
  7.     }
  8.   ]
  9. }
  10. // en_US/element/string.json 英文
  11. {
  12.   "string": [
  13.     {
  14.       "name": "title",
  15.       "value": "1000phone"
  16.     }
  17.   ]
  18. }
  19. // zh_CN/element/string.json 中文
  20. {
  21.   "string": [
  22.     {
  23.       "name": "title",
  24.       "value": "柏木白"
  25.     }
  26.   ]
  27. }
  28. //页面中访问
  29. Text($r("app.string.title"))
  30. // 配置文件中
  31. "label": "$string:title"

```

声明配置

为使应用能够正常使用`UIAbility`,需要在`module.json5`配置文件的`abilities`标签中声明`UIAbility`的名称、入口、标签等相关信息。

```js

  1. {
  2.   "module": {
  3.     // ...
  4.     "abilities": [
  5.       {
  6.         "name": "EntryAbility", // UIAbility组件的名称
  7.         "srcEntrance": "./ets/entryability/EntryAbility.ts", // UIAbility组件的代码路径
  8.         "description": "$string:EntryAbility_desc", // UIAbility组件的描述信息
  9.         "icon": "$media:icon", // UIAbility组件的图标
  10.         "label": "$string:EntryAbility_label", // UIAbility组件的标签
  11.         "startWindowIcon": "$media:icon", // UIAbility组件启动页面图标资源文件的索引
  12.         "startWindowBackground": "$color:start_window_background", // UIAbility组件启动页面背景颜色资源文件的索引
  13.         // ...
  14.       }
  15.     ]
  16.   }
  17. }

```

二、生命周期

当用户打开、切换和返回到对应应用时,应用中的`UIAbility`实例会在其生命周期的不同状态之间转换。
`UIAbility`的生命周期包括`Create`、`Foreground`、`Background`、`Destroy`四个状态,如下图所示。

`Create`状态为在应用加载过程中,`UIAbility`实例创建完成时触发,系统会调用`onCreate()`回调。可以在该回调中进行应用初始化操作,例如变量定义资源加载等,用于后续的UI界面展示。

```js

  1. import UIAbility from '@ohos.app.ability.UIAbility';
  2. import Window from '@ohos.window';
  3. export default class EntryAbility extends UIAbility {
  4.     onCreate(want, launchParam) {
  5.       // 应用初始化
  6.       // want中包含如包代码路径、Bundle名称、Ability名称和应用程序需要的环境状态等属性信息
  7.       // launchParam包含启动参数 主要在被其他UIAbility唤起时使用
  8.       // 可通过this.context访问上下文对象 后续会做讲解
  9.     }
  10.     // ...
  11. }

```

`UIAbility`实例创建完成之后,在进入`Foreground`之前,系统会创建一个`WindowStage`。`WindowStage`创建完成后会进入`onWindowStageCreate()`回调,可以在该回调中设置UI界面加载、设置`WindowStage`的事件订阅。

在`onWindowStageCreate()`回调中通过`loadContent()`方法设置应用要加载的页面并根据需要订阅WindowStage的事件(获焦/失焦、可见/不可见)。

```js

  1. import UIAbility from '@ohos.app.ability.UIAbility';
  2. import Window from '@ohos.window';
  3. export default class EntryAbility extends UIAbility {
  4.     onWindowStageCreate(windowStage: Window.WindowStage) {
  5.         // 设置WindowStage的事件订阅(获焦/失焦、可见/不可见)
  6.         // 设置UI界面加载
  7.         windowStage.loadContent('pages/Index', (err, data) => {
  8.             // ...
  9.         });
  10.     }
  11. }

```

对应于`onWindowStageCreate()`回调。在`UIAbility`实例销毁之前,则会先进入`onWindowStageDestroy()`回调,可以在该回调中释放UI界面资源。例如在`onWindowStageDestroy()`中注销获焦/失焦等`WindowStage`事件。

```js

  1. import UIAbility from '@ohos.app.ability.UIAbility';
  2. import Window from '@ohos.window';
  3. export default class EntryAbility extends UIAbility {
  4.     onWindowStageDestroy() {
  5.         // 释放UI界面资源
  6.     }
  7. }

```

`Foreground`和`Background`状态分别在`UIAbility`实例切换至前台和切换至后台时触发,对应于`onForeground()`回调和`onBackground()`回调。

- `onForeground()`回调,在`UIAbility`的UI界面可见之前,如`UIAbility`切换至前台时触发。可以在`onForeground()`回调中申请系统需要的资源,或者重新申请在`onBackground()`中释放的资源。

- `onBackground()`回调,在`UIAbility`的UI界面完全不可见之后,如`UIAbility`切换至后台时候触发。可以在`onBackground()`回调中释放UI界面不可见时无用的资源,或者在此回调中执行较为耗时的操作,例如状态保存等。

```js

  1. import UIAbility from '@ohos.app.ability.UIAbility';
  2. export default class EntryAbility extends UIAbility {
  3.     onForeground() {
  4.         // 申请系统需要的资源,或者重新申请在onBackground中释放的资源
  5.     }
  6.     onBackground() {
  7.         // 释放UI界面不可见时无用的资源,或者在此回调中执行较为耗时的操作
  8.         // 例如状态保存等
  9.     }
  10. }
  11. ```
  12. `Destroy`状态在`UIAbility`实例销毁时触发。可以在`onDestroy()`回调中进行系统资源的释放、数据的保存等操作。
  13. ```js
  14. import UIAbility from '@ohos.app.ability.UIAbility';
  15. import Window from '@ohos.window';
  16. export default class EntryAbility extends UIAbility {
  17.     onDestroy() {
  18.         // 系统资源的释放、数据的保存等
  19.     }
  20. }

```

三、与UI界面同步


> 有时我们需要让`UIAbility`组件与UI界面之间进行通信,例如:通知UI界面应用的前后台切换、多`module`下的UI界面传值、传递启动参数等。

基于HarmonyOS的应用模型,可以通过以下两种方式来实现:
- `EventHub`:基于发布订阅模式来实现,事件需要先订阅后发布,订阅者收到消息后进行处理。
- `globalThis`:`ArkTS`引擎实例内部的一个全局对象,在`ArkTS`引擎实例内部都能访问。
- ​​使用`AppStorage`/`LocalStorage`进行数据同步​​:`ArkUI`提供了`AppStorage`和`LocalStorage`两种应用级别的状态管理方案,可用于实现应用级别和`UIAbility`级别的数据同步。

EventHub


主要使用上下文对象中的`eventHub`模块,提供了事件中心,提供订阅、取消订阅、触发事件的能力。
```js

  1. //UIAbility组件中访问上下文
  2. export default class EntryAbility extends UIAbility {
  3.     onCreate() {
  4.       // 创建之后 可通过this.context访问上下文对象
  5.     }
  6. }
  7. // UI页面访问上下文对象
  8. import common from '@ohos.app.ability.common';
  9. @Entry
  10. @Component
  11. struct Index {
  12.   private context = getContext(this) as common.UIAbilityContext;
  13. }


```


通信示例


```js

  1. //UIAbility组件---------------------------
  2. export default class EntryAbility extends UIAbility {
  3.   onCreate(){
  4.     this.context.eventHub.on("UIEmit",(data)=>{
  5.       //通知其他Ability
  6.     })
  7.   }
  8.   onForeground() {
  9.     // 触发自定义事件 第一个参数:事件名 后续参数作为监听回调的参数
  10.     // 切至前台 通知页面启动计时器
  11.     this.context.eventHub.emit("EventName","start")
  12.   }
  13.   onBackground() {
  14.     // 切至后台 通知页面停止计时器
  15.     this.context.eventHub.emit("EventName","stop")
  16.   }
  17. }
  18. // UI界面--------------------------------
  19. import common from '@ohos.app.ability.common';
  20. @Entry
  21. @Component
  22. struct Index {
  23.   private context = getContext(this) as common.UIAbilityContext;
  24.   @State num:number=0;
  25.   private timer;
  26.   aboutToAppear(){
  27.     this.start();
  28.     //监听前后台切换事件
  29.     this.context.eventHub.on("EventName",(data)=>{
  30.       this[data]()
  31.     })
  32.   }
  33.   start(){
  34.     this.timer=setInterval(()=>{
  35.       this.num++;
  36.     },1000)
  37.   }
  38.   stop(){
  39.     clearInterval(this.timer);
  40.   }
  41.   build() {
  42.     Column(){
  43.       Text(this.num.toString())
  44.       .width("100%")
  45.       .height("100%")
  46.       .fontSize(30)
  47.       .textAlign(TextAlign.Center)
  48.       Button(){
  49.         Text("页面触发事件")
  50.       }
  51.       .onClick(()=>{
  52.         // 向UIAbility发射信号
  53.         this.context.eventHub.emit("UIEmit","params");
  54.       })
  55.     }
  56.   }
  57. }


```


globalThis


`globalThis`是​`​ArkTS`引擎实例​​内部的一个全局对象,引擎内部的`UIAbility/ExtensionAbility/Page`都可以使用,因此可以使用`globalThis`对象进行数据同步。


```js

  1. //UIAbility组件---------------------------
  2. export default class EntryAbility extends UIAbility {
  3.     onCreate(want, launch) {
  4.         globalThis.title="柏木白";
  5.     }
  6. }
  7. // UI界面--------------------------------
  8. @Entry
  9. @Component
  10. struct Index {
  11.   aboutToAppear() {
  12.     console.log(globalThis)
  13.   }
  14. }


```
<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

  1. export default class EntryAbility extends UIAbility {
  2.   // 创建LocalStorage实例
  3.   storage: LocalStorage = new LocalStorage({
  4.     title:"柏木白"
  5.   })
  6.    onWindowStageCreate(windowStage: window.WindowStage) {
  7.     // 注入页面
  8.     windowStage.loadContent('pages/Index',this.storage)
  9.   }
  10. }


```
在UI页面通过GetShared接口获取在通过loadContent共享的LocalStorage实例:
```js

  1. // 通过GetShared接口获取stage共享的LocalStorage实例
  2. let storage = LocalStorage.GetShared()
  3. @Entry(storage)
  4. @Component
  5. struct CompA {
  6.   //组件内部可以用@LocalStorageLink/Prop接收
  7.   @LocalStorageLink('title') title: string="";
  8.   build() {
  9.     Column() {
  10.       Text(this.title).fontSize(50)
  11.     }
  12.   }
  13. }


```
<span style="color:red">---- 注意:存储在预览视图中无效 需要用模拟器或真机调试 -----</span>


AppStorage


`AppStorage`是应用级的全局状态共享,相当于整个应用的“中枢”,是在应用启动的时候会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。

随时随地设置:
```js

  1. export default class EntryAbility extends UIAbility {
  2.   onCreate(){
  3.     AppStorage.SetOrCreate("name","周杰伦")
  4.   }
  5. }


```
在UI页面通过`@StorageLink`访问:
```js

  1. @Entry
  2. @Component
  3. struct Storage {
  4.   @StorageLink('name') name: string = "";
  5.   build() {
  6.     Row() {
  7.       Column() {
  8.         Text(this.name)
  9.           .fontSize(50)
  10.           .fontWeight(FontWeight.Bold)
  11.       }
  12.       .width('100%')
  13.     }
  14.     .height('100%')
  15.   }
  16. }


```

四、应用内UIAbility间跳转


> `UIAbility`组件是系统调度的基本单元,为应用提供绘制界面的窗口;一个`UIAbility`组件中可以通过多个页面来实现一个功能模块。每一个`UIAbility`组件实例,都对应于一个最近任务列表中的任务。

对于开发者而言,可以根据具体场景选择单个还是多个`UIAbility`,划分建议如下:
 * 如果希望在任务视图中看到一个任务,则建议使用一个`UIAbility`,多个页面的方式。 
  * 如果希望在任务视图中看到多个任务,或者需要同时开启多个窗口,则建议使用多个`UIAbility`开发不同的模块功能。
  * `Entry module`是项目主模块,当应用比较大时,可以拆分`Feature module`,配置成按需下载安装,不同设备下也可以随意搭配组合。

1.新建`module`


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

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

2.在`entry`的页面中编写跳转逻辑:


```js

  1. import common from '@ohos.app.ability.common';
  2. @Entry
  3. @Component
  4. struct Index {
  5.   // 获取上下文对象
  6.   private context = getContext(this) as common.UIAbilityContext;
  7.   build() {
  8.     Navigation(){
  9.       Flex({justifyContent:FlexAlign.Center,alignItems:ItemAlign.Center}){
  10.         Button({type:ButtonType.Capsule}){
  11.           Text("跳转").fontColor("white")
  12.         }
  13.         .padding(15)
  14.         .width(150)
  15.         .onClick(()=>{
  16.           //跳转至指定Ability
  17.           this.context.startAbility({
  18.             deviceId:"",//设备id 留空表示当前设备
  19.             bundleName:"com.example.myapplication",//包名 可以从AppScope/app.json5中拿到
  20.             moduleName:"application",//模块名
  21.             abilityName:"ApplicationAbility",
  22.             parameters:{//携带的参数 可以借助参数指定目标Ability启动的页面
  23.               val:"柏木白"
  24.             }
  25.           })
  26.         })
  27.       }
  28.       .width("100%")
  29.       .height("100%")
  30.     }
  31.     .title("入口模块首页")
  32.   }
  33. }


```
跳转目标接收参数 在页面中显示:

```js

  1. //UIAbility组件 ------------------------------------------
  2. import UIAbility from '@ohos.app.ability.UIAbility';
  3. import window from '@ohos.window';
  4. export default class ApplicationAbility extends UIAbility {
  5.   onCreate(want, launchParam) {
  6.     // 接收参数
  7.     globalThis.title=want.parameters.title;
  8.   }
  9.   onWindowStageCreate(windowStage: window.WindowStage) {
  10.     windowStage.loadContent('pages/Index');
  11.   }
  12. }
  13. //UI界面-------------------------------------------------
  14. @Entry
  15. @Component
  16. struct Index {
  17.   build() {
  18.     Row() {
  19.       Column() {
  20.         Text("接收到的数据:"+globalThis.title)
  21.           .fontSize(20)
  22.           .fontWeight(FontWeight.Bold)
  23.       }
  24.       .width('100%')
  25.     }
  26.     .height('100%')
  27.   }
  28. }


```

3.预览调试


编辑部署配置:

选择部署多个hap包:

运行效果:


 

4.返回并携带参数


目标Ability中的页面,调用terminateSelfWithResult()方法,入参为需要返回给EntryAbility的信息:
```js

  1. import common from '@ohos.app.ability.common';
  2. @Entry
  3. @Component
  4. struct Index {
  5.   // 获取上下文对象
  6.   private context = getContext(this) as common.UIAbilityContext;
  7.   build() {
  8.     Row() {
  9.       Column() {
  10.         Text("接收到的数据:"+globalThis.title)
  11.           .fontSize(20)
  12.           .fontWeight(FontWeight.Bold)
  13.         Button({type:ButtonType.Capsule}){
  14.           Text("返回").fontColor("white")
  15.         }
  16.         .padding(15)
  17.         .margin(10)
  18.         .width(150)
  19.         .onClick(()=>{
  20.           let abilityResult = {
  21.             resultCode: 200,
  22.             want: {
  23.               parameters: {
  24.                 title:"鸿蒙生态"
  25.               },
  26.             },
  27.           }
  28.           //停止自身 并携带数据返回
  29.           this.context.terminateSelfWithResult(abilityResult);
  30.         })
  31.       }
  32.       .width('100%')
  33.     }
  34.     .height('100%')
  35.   }
  36. }


```
调用方跳转方法改为`startAbilityForResult`,通过回调访问结果:
```js

  1. //跳转至指定Ability并要接收结果
  2. this.context.startAbilityForResult({
  3.   deviceId:"",//设备id 留空表示当前设备
  4.   bundleName:"com.example.myapplication",//包名 可以从AppScope/app.json5中拿到
  5.   moduleName:"application",//模块名
  6.   abilityName:"ApplicationAbility",
  7.   parameters:{
  8.     title:"柏木白"
  9.   }
  10. })
  11.   .then((data:any) => {
  12.     //接收目标销毁时返回的数据
  13.     console.log(data.want.parameters.title,"-----------------");
  14.   })


```

5.目标`UIAbility`是否首次启动


目标`UIAbility`首次启动时,在目标`UIAbility`的`onWindowStageCreate()`生命周期回调中,解析`EntryAbility`传递过来的want参数,获取到需要加载的页面信息`url`,传入`windowStage.loadContent()`方法。
```js

  1.  onWindowStageCreate(windowStage: Window.WindowStage) {
  2.         let url =this.context.want.parameters.url||'pages/Index';
  3.         windowStage.loadContent(url);
  4.     }


```
目标`UIAbility`非首次启动,在目标`UIAbility`中,默认加载的是`Index`页面。由于当前`UIAbility`实例之前已经创建完成,此时会进入`UIAbility`的`onNewWant()`回调中且不会进入`onCreate()`和`onWindowStageCreate()`生命周期回调,在`onNewWant()`回调中解析调用方传递过来的`want`参数,并挂在到全局变量`globalThis`中,以便于后续在页面中获取。
```js

  1. //增加onNewWant钩子
  2. onNewWant(want, launchParam) {
  3.   // 接收调用方UIAbility传过来的参数
  4.   globalThis.url = want.parameters.url;
  5. }


```
目标`UIAbility`首页中判断跳转:
```js

  1. import router from '@ohos.router';
  2. @Entry
  3. @Component
  4. struct Index {
  5.   onPageShow() {
  6.     if (globalThis.url) {
  7.       router.replaceUrl({
  8.         url: globalThis.url,
  9.       })
  10.     }
  11.   }
  12. }


```

五、启动模式


`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

  1. {
  2.   "module": {
  3.     ...
  4.     "abilities": [
  5.       {
  6.         "launchType": "singleton",
  7.         ...
  8.       }
  9.     ]
  10.   }
  11. }


```


2. `multiton`启动模式


`multiton`启动模式为多实例模式,每次调用`startAbility()`方法时,都会在应用进程中创建一个新的该类型`UIAbility`实例。即在最近任务列表中可以看到有多个该类型的`UIAbility`实例。这种情况下可以将`UIAbility`配置为`multiton`(多实例模式)。

`multiton`启动模式的开发使用,在`module.json5`配置文件中的`launchType`字段配置为`multiton`即可。
```js

  1. {
  2.   "module": {
  3.     ...
  4.     "abilities": [
  5.       {
  6.         "launchType": "multiton",
  7.         ...
  8.       }
  9.     ]
  10.   }
  11. }


```


3. `specified`启动模式


`specified`启动模式为指定实例模式,针对一些特殊场景使用(例如文档应用中每次新建文档希望都能新建一个文档实例,重复打开一个已保存的文档希望打开的都是同一个文档实例)。

`specified`启动模式的开发使用,在`module.json5`配置文件中的`launchType`字段配置为`specified`即可。
```js

  1. {
  2.   "module": {
  3.     ...
  4.     "abilities": [
  5.       {
  6.         "launchType": "specified",
  7.         ...
  8.       }
  9.     ]
  10.   }
  11. }


```
在`EntryAbility`中,调用`startAbility()`方法时,在`want`参数中,增加一个自定义参数来区别`UIAbility`实例,例如增加一个"instanceKey"自定义参数。
```js

  1. // 在启动指定实例模式的UIAbility时,给每一个UIAbility实例配置一个独立的Key标识
  2. // 例如在文档使用场景中,可以用文档路径作为Key标识
  3. function getInstance() {
  4.     // return id
  5. }
  6. let want = {
  7.     deviceId: '', // deviceId为空表示本设备
  8.     bundleName: 'com.example.myapplication',
  9.     abilityName: 'FuncAbility',
  10.     moduleName: 'module1', // moduleName非必选
  11.     parameters: { // 自定义信息
  12.         instanceKey: getInstance(),
  13.     },
  14. }
  15. // context为调用方UIAbility的AbilityContext
  16. this.context.startAbility(want).then(() => {
  17.     // ...
  18. }).catch((err) => {
  19.     // ...
  20. })


```
目标`UIAbility`:
```js

  1. import AbilityStage from '@ohos.app.ability.AbilityStage';
  2. export default class MyAbilityStage extends AbilityStage {
  3.     onAcceptWant(want): string {
  4.         // 在被调用方的AbilityStage中,针对启动模式为specified的UIAbility返回一个UIAbility实例对应的一个Key值
  5.         return want.parameters.instanceKey
  6.     }
  7. }


```

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/664665
推荐阅读
相关标签
  

闽ICP备14008679号