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

//页面中

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


```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>