HarmonyOS应用开发——程序框架UIAbility、启动模式与路由跳转

前言

UIAbility简单来说就是一种包含用户界面的应用组件,用于和用户进行交互。每一个UIAbility实例,对应于一个最近任务列表中的任务。

一个应用可以有一个UIAbility,也可以有多个UIAbility。一个UIAbility可以对应于多个页面,建议将一个独立的模块放到一个UIAbility中,以页面的形式呈现。

HarmonyOS应用开发——程序框架UIAbility、启动模式与路由跳转_第1张图片

先看下通过应用程序框架UIAbility、启动模式和路由参数跳转,我们实现的简单Demo效果图:

HarmonyOS应用开发——程序框架UIAbility、启动模式与路由跳转_第2张图片


一、UIAbility的生命周期

UIAbility的生命周期包括CreateForegroundBackgroundDestroy四个状态;

WindowStageCreateWindowStageDestroy为窗口管理器(WindowStage)在UIAbility 中管理UI界面功能的两个生命周期回调,从而实现 UIAbility 与窗口之间的弱耦合。

1、UIAbility的生命周期

HarmonyOS应用开发——程序框架UIAbility、启动模式与路由跳转_第3张图片

在新建空工程后,会发现在entry/src/main/ets/entryability/目录下有一个EntryAbility.ts文件,它对应于entry/src/main/ets/pages目录下的Index.ets:

import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
  onCreate(want, launchParam) {
    // 应用初始化
  }

  onDestroy() {
    // onDestroy()在UIAbility实例销毁时触发
    // 可以用于资源的释放,数据的保存等
  }

  onWindowStageCreate(windowStage: window.WindowStage) {
    // 设置UI页面加载
    // 设置WindowState的事件订阅(获焦/失焦、可见/不可见)
    // Main window is created, set main page for this ability
  
    // 使用 windowStage.loadContent设置要加载的页面,并根据需要订阅WindowState的事件
    // windowStage.loadContent('pages/Index', (err, data) => {
    windowStage.loadContent('pages/StudentListPage', (err, data) => {
      if (err.code) {
 
        return;
      }
    });
  }

  onWindowStageDestroy() {
    // 释放UI界面资源
  }

  onForeground() {
    // 申请系统需要的资源,或者重新申请在onBackground中释放的资源
  }

  onBackground() {
    // 释放UI页面不可见时无用的资源,或者在此回调中执行较为耗时的操作(例如状态保存等)
  }
}

(1)、Create状态

UIAbility 实例创建时触发,系统会调用 onCreate() 回调,可以在 onCreate() 回调中进行相关初始化操作。

import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
    onCreate(want, launchParam) {
        // 应用初始化
        ...
    }
    ...
}

(2)、Forground状态

Forground 状态在 UIAbility 切换至前台时触发,对应 onForground() 回调,它会在 UIAbility 的UI页面可见之前触发(即UIAbility切换至前台的时候触发)。在 onForground() 回调中,可以申请系统需要的资源,或者重新申请在 onBackground() 中释放的资源。

import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
    ...

    onForeground() {
        // 申请系统需要的资源,或者重新申请在onBackground中释放的资源
        ...
    }
}

(3)、Background状态

Background状态在UIAbility切换至后台时触发,对应onBackground()回调,它会在UIAbility完全不可见之后触发(即UIAbility切换至后台的时候触发)。在onBackground()回调中可以释放UI页面不可见时无用的资源,或者在此回调中执行较为好使的操作,例如状态保存等。

import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
    ...
    
    onBackground() {
        // 释放UI页面不可见时无用的资源,或者在此回调中执行较为耗时的操作
        // 例如状态保存等
        ...
    }
}

(4)、Destroy状态

Destroy状态,在UIAbility销毁时触发,对应onDestroy()回调,可以在其中进行系统资源的释放、数据的保存等操作。

import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
    ...

    onDestroy() {
        // 系统资源的释放、数据的保存等
        ...
    }
}

2、WindowStage窗口管理器

为了实现多设备形态上的裁剪和多窗口的可扩展性,系统对组件管理和窗口管理进行了解耦。UIAbility的生命周期包括Create、Foreground、Background、Destroy四个状态,WindowStageCreate和WindowStageDestroy为窗口管理器(WindowStage)在UIAbility中管理UI界面功能的两个生命周期回调,从而实现UIAbility与窗口之间的弱耦合。

(1)、onWindowStageCreate()

在UIAbility实例创建完成之后,在进入Forground状态之前,系统会创建一个WindowState。 每一个UIAbility实例都对应持有一个WindowState实例。

WindowState为本地窗口管理器,用于管理窗口相关的内容,例如与界面相关的获焦/失焦、可见/不可见。

可以在onWindowsTATECreate()回调中设置UI页面加载、设置WindowState的事件订阅。在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) {
        // 设置UI页面加载
        // 设置WindowStage的事件订阅(获焦/失焦、可见/不可见)
        ...

        windowStage.loadContent('pages/Index', (err, data) => {
            ...
        });
    }
    ...
}

(2)、onWindowStageDestroy()

对应于onWindowStageCreate()回调,在UIAbility实例销毁之前,会进入onWindowStageDestroy()回调,可以在其中释放UI页面资源。

import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
    ...

    onWindowStageDestroy() {
        // 释放UI页面资源
        ...
    }
}

二、UIAbility的启动模式

UIAbility当前支持 singleton(单实例模式)、multiton(多实例模式)和 specified(指定实例模式) 3种启动模式.

1、singleton(单实例模式)

每次调用startAbility()方法时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例,系统中只存在唯一一个该UIAbility实例。

即在最近任务列表中只存在一个该类型的UIAbility实例。

singleton启动模式,每次调用startAbility()启动UIAbility时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例,系统中只存在唯一一个该UIAbility实例。

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

{
   "module": {
     ...
     "abilities": [
       {
         "launchType": "singleton",
         ...
       }
     ]
  }
}

2、multiton(多实例模式)

用户在使用分屏功能时,希望使用两个不同应用(例如备忘录应用和图库应用)之间进行分屏,也希望能使用同一个应用(例如备忘录应用自身)进行分屏。

这种情况下可以将UIAbility配置为multiton(多实例模式)。每次调用startAbility()方法时,都会在应用进程中创建一个该类型的UIAbility实例。

即在最近任务列表中可以看到有多个该类型的UIAbility实例。

multiton启动模式,每次调用startAbility()方法时,都会在应用进程中创建一个该类型的UIAbility实例。

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

{
   "module": {
     ...
     "abilities": [
       {
         "launchType": "multiton",
         ...
       }
     ]
  }
}

3、specified(指定实例模式)

用户打开文档应用,从文档应用中打开一个文档内容,回到文档应用,继续打开同一个文档,希望打开的还是同一个文档内容;以及在文档应用中新建一个新的文档,每次新建文档,希望打开的都是一个新的空白文档内容。

这种情况下可以将UIAbility配置为specified(指定实例模式)。在UIAbility实例新创建之前,允许开发者为该实例创建一个字符串Key,新创建的UIAbility实例绑定Key之后,后续每次调用startAbility方法时,都会询问应用使用哪个Key对应的UIAbility实例来响应startAbility请求。如果匹配有该UIAbility实例的Key,则直接拉起与之绑定的UIAbility实例,否则创建一个新的UIAbility实例。运行时由UIAbility内部业务决定是否创建多实例。

specified启动模式,根据业务需要是否创建一个新的UIAbility实例。在UIAbility实例创建之前,会先进入AbilityStage的onAcceptWant回调,在onAcceptWant回调中为每一个UIAbility实例创建一个Key,后续每次调用startAbility()方法创建该类型的UIAbility实例都会询问使用哪个Key对应的UIAbility实例来响应startAbility()请求。

三、UIAbility内页面的跳转和数据传递

UIAbility的数据传递包括有 UIAbility内页面的跳转和数据传递UIAbility间的数据跳转和数据传递,本章节主要讲解UIAbility内页面的跳转和数据传递。

在一个应用包含一个UIAbility的场景下,可以通过新建多个页面来实现和丰富应用的内容。这会涉及到UIAbility内页面的新建以及UIAbility内页面的跳转和数据传递。

接下来就继续完善我们的"学生列表",实现点击Item右侧的"Next"按钮,跳转进入学生详情页面并传入当前学生name, 在学生详情页面底部有一个Button,点击跳转回学生列表页面,并且传递一个参数给上一页面学生列表页面,并显示UI。

HarmonyOS应用开发——程序框架UIAbility、启动模式与路由跳转_第4张图片

页面间的导航可以通过页面路由router模块来实现。页面路由模块根据页面url找到目标页面,从而实现跳转。通过页面路由模块,可以使用不同的url访问不同的页面,包括跳转到UIAbility内的指定页面、用UIAbility内的某个页面替换当前页面、返回上一页面或指定的页面等。

1、FirstPage

将我们的Index.ts文件下的StudentListPage.ts设置为启动页面,在src/main/ets/entryability/EntryAbility.ts文件中的onWindowStageCreate()回调中进行修改, 使用 windowStage.loadContent设置要加载的页面 windowStage.loadContent('pages/StudentListPage', (err, data)

import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
  
  onWindowStageCreate(windowStage: window.WindowStage) {
    // 设置UI页面加载
    // 设置WindowState的事件订阅(获焦/失焦、可见/不可见)
    // Main window is created, set main page for this ability

    // 使用 windowStage.loadContent设置要加载的页面,并根据需要订阅WindowState的事件
    // windowStage.loadContent('pages/Index', (err, data) => {
    windowStage.loadContent('pages/StudentListPage', (err, data) => {
      if (err.code) {
       
        return;
      }
     
    });
  }
}

StudentListPage.ts加入@Entry装饰符

import DataModel from '../viewmodel/DataModel';
import StudentListItem from '../view/StudentListItem';
import router from '@ohos.router';

const TAG = '[StudentListPage]';

@Entry
@Component
export struct StudentListPage {
	...
}

(1)、页面跳转和参数接收

在使用页面路由之前,需要先导入 router 模块:

import router from '@ohos.router';

(2)、配置路由

entry/src/main/resources/base/profile/main_pages.json 文件中配置页面路由信息:

{
  "src": [
    "pages/Index",
    "pages/StudentDetailPage",
    "pages/StudentListPage"
  ]
}

(3)、跳转路由并传递参数

StudentListPage的组件StudentListItem中,设置“Next” Image的点击事件:

Image($r('app.media.ic_arrow_next'))
        .width('30vp')
        .height('30vp')
        .margin('30vp')
        .onClick(() => {
          console.log('Next Click' + this.name);

          router.pushUrl({
            // url: 'pages/StudentDetailPage',
            url: CommonConstants.STUDENT_DETAIL_URL,
            params: {
              // 自定义的参数"name"
              name: this.name,
            }
          }).catch((error) => {
            console.log('Next Click', 'IndexPage push error' + JSON.stringify(error));
          })
        })

2、SecondPage

在src/main/ets/pages目录下,右键,新建一个StudentDetailPage页面,用于实现页面间的跳转和数据传递。

(1)、接收路由跳转传递进来的参数

在StudentDetailPage.ts文件中,通过调用router.getParams()方法获取StudentListPage页面传递过来的自定义参数。

import router from '@ohos.router'

const TAG = '[StudentDetailPage]';

@Entry
@Component
struct StudentDetailPage {

  // 接收到的参数,因为UI要跟着参数进行变化,所以需要加@State装饰符
  @State name: string = (router.getParams() as Record)['name'];

  aboutToAppear() {
    console.log(TAG, 'StudentDetailPage接收的参数是: ' + this.name);
  }

  build() {
    Column({ space: 16 }) {
      Text(this.name)
        ...
        .textAlign(TextAlign.Center)

      Text(this.name.repeat(100))
        .margin({
          top: '30vp',
          bottom: '30vp',
        })
  }
}

(2)、页面返回传递参数、上一页面接收参数

在StudentDetailPage 页面中,可以通过调用router.back()方法实现返回到上一个页面,或者在调用router.back()方法时增加可选的options参数(增加url参数)返回到指定页面。

  • 直接返回商议页面:
router.back();
  • 返回到指定页面、传参:
// 调用router.back()方法返回上一个页面或者返回指定页面时,根据需要继续增加自定义参数,例如在返回时增加一个自定义参数src

router.back({
    url: 'pages/StudentListPage',
    params: {
        backMessage: this.backMessage,
     }
})

在上一页面StudentListPage中接收返回传递进来的参数: 调用router.back()方法,不会新建页面,返回的是原来的页面,在原来页面中@State声明的变量不会重复声明,以及也不会触发页面的aboutToAppear()生命周期回调,因此无法直接在变量声明以及页面的aboutToAppear()生命周期回调中接收和解析router.back()传递过来的自定义参数

需要在onPageShow() 中解析传递回来的参数

import DataModel from '../viewmodel/DataModel';
import StudentListItem from '../view/StudentListItem';
import router from '@ohos.router';

const TAG = '[StudentListPage]';

@Entry
@Component
export struct StudentListPage {
  private studentList: Array = [];
  
  @State backMessage: string = '';

  // 调用router.back()方法,不会新建页面,返回的是原来的页面,在原来页面中@State声明的变量不会重复声明,
  // 以及也不会触发页面的aboutToAppear()生命周期回调,因此无法直接在变量声明以及页面的aboutToAppear()
  // 生命周期回调中接收和解析router.back()传递过来的自定义参数。
  onPageShow() {
    this.backMessage = router.getParams()?.['backMessage'];
    console.log(TAG, 'StudentDetailPage返回数据:StudentListPage => ' + this.backMessage)
  }

  build() {
    Column({ space: 16 }) {
      
      ...

      if (this.backMessage != null && this.backMessage != "") {
        Text('返回数据:' + this.backMessage)
          .fontSize('30fp')
          .fontColor(Color.Red)
          .width('100%')
          .margin({
            top: '100vp',
            left: '30vp'
          })
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.page_background'))
  }
}

至此,实现了UIAbility内页面间的跳转并传参的简单实例。

3、代码

以下给出本实例【UIAbility内页面间的跳转并传参】的全部代码:

(1)、路由配置

在entry/src/main/resources/base/profile/main_pages.json中配置路由页面信息:

import router from '@ohos.router';
import CommonConstants from '../common/constants/CommonConstants';

@Component
export default struct StudentListItem {
  @State isChecked: boolean = false;
  private name?: string;

  @Builder checkIcon(icon: Resource) {
    Image(icon)
      .objectFit(ImageFit.Contain)
      .width($r('app.float.checkbox_width'))
      .height($r('app.float.checkbox_height'))
      .margin($r('app.float.checkbox_margin'))
  }

  build() {
    Row() {
      if (this.isChecked) {
        this.checkIcon($r('app.media.ic_checked'))
      } else {
        this.checkIcon($r('app.media.ic_unchecked'))
      }

      Text(this.name)
        .fontColor(this.isChecked ? Color.Red : Color.Black)
        .fontSize(this.isChecked ? $r('app.float.item_checked_font_size') : $r('app.float.item_font_size'))
        .fontWeight(500)
        .opacity(this.isChecked ? 0.5 : 1.0)
        .decoration({ type: this.isChecked ? TextDecorationType.LineThrough : TextDecorationType.None })

      Blank()

      Image($r('app.media.ic_arrow_next'))
        .width('30vp')
        .height('30vp')
        .margin('30vp')
        .onClick(() => {
          console.log('Next Click' + this.name);

          router.pushUrl({
            // url: 'pages/StudentDetailPage',
            url: CommonConstants.STUDENT_DETAIL_URL,
            params: {
              name: this.name,
            }
          }).catch((error) => {
            console.log('Next Click', 'IndexPage push error' + JSON.stringify(error));
          })
        })
    }
    .borderRadius(22)
    .backgroundColor($r('app.color.start_window_background'))
    .width('100%')
    .width('100%')
    .height($r('app.float.list_item_height'))
    .onClick(() => {
      this.isChecked = !this.isChecked;
    })
  }
}

学生列表页面StudentListPage:

import DataModel from '../viewmodel/DataModel';
import StudentListItem from '../view/StudentListItem';
import router from '@ohos.router';

const TAG = '[StudentListPage]';

@Entry
@Component
export struct StudentListPage {
  private studentList: Array = [];
  @State backMessage: string = '';

  // 调用router.back()方法,不会新建页面,返回的是原来的页面,在原来页面中@State声明的变量不会重复声明,
  // 以及也不会触发页面的aboutToAppear()生命周期回调,因此无法直接在变量声明以及页面的aboutToAppear()
  // 生命周期回调中接收和解析router.back()传递过来的自定义参数。
  onPageShow() {
    this.backMessage = router.getParams()?.['backMessage'];
    console.log(TAG, 'StudentDetailPage返回数据:StudentListPage => ' + this.backMessage)
  }

  aboutToAppear() {
    this.studentList = DataModel.getStudentList();
    // this.backMessage = router.getParams()?.['backMessage'];
  }

  build() {
    Column({ space: 16 }) {
      Text($r("app.string.Student_List_Title"))
        .fontSize('28fp')
        .fontWeight(FontWeight.Bold)
        .lineHeight('33vp')
        .width('100%')
        .margin({
          top: '24vp',
          bottom: '12vp',
        })
        .textAlign(TextAlign.Center)

      ForEach(this.studentList, (item: string) => {
        StudentListItem({ name: item })
      }, (item: string) => JSON.stringify(item))


      if (this.backMessage != null && this.backMessage != "") {
        Text('返回数据:' + this.backMessage)
          .fontSize('30fp')
          .fontColor(Color.Red)
          .width('100%')
          .margin({
            top: '100vp',
            left: '30vp'
          })
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.page_background'))
  }
}

(2)、学生详情界面

import router from '@ohos.router'

const TAG = '[StudentDetailPage]';

@Entry
@Component
struct StudentDetailPage {

  // 接收到的参数,因为UI要跟着参数进行变化,所以需要加@State装饰符
  @State name: string = (router.getParams() as Record)['name'];

  // 点击Button-Back返回给上个页面一个参数
  @State backMessage: string = this.name + "最帅!!!"

  aboutToAppear() {
    console.log(TAG, 'StudentDetailPage接收的参数是: ' + this.name);
  }

  build() {
    Column({ space: 16 }) {
      Text(this.name)
        .fontSize('28fp')
        .fontColor(Color.Blue)
        .fontWeight(FontWeight.Bold)
        .lineHeight('33vp')
        .width('100%')
        .margin({
          top: '24vp',
          bottom: '12vp',
        })
        .textAlign(TextAlign.Center)

      Text(this.name.repeat(100))
        .margin({
          top: '30vp',
          bottom: '30vp',
        })

      // 可以通过调用router.back()方法实现返回到上一个页面,
      // 或者在调用router.back()方法时增加可选的options参数(增加url参数)返回到指定页面
      Blank()

      Button('返回')
        .fontSize('16fp')
        .width('300vp')
        .height('35vp')
        .backgroundColor($r('app.color.page_background2'))
        .onClick(() => {
          // 返回上一个页面
          // router.back();

          // 返回到指定页面
          // router.back({ url: 'pages/StudentListPage' });

          // 调用router.back()方法返回上一个页面或者返回指定页面时,根据需要继续增加自定义参数,例如在返回时增加一个自定义参数src
          router.back({
            url: 'pages/StudentListPage',
            params: {
              backMessage: this.backMessage,
            }
          })

        })
    }
    .width('100%')
    .height('100%')
    .padding('16vp')
    .backgroundColor($r('app.color.page_background'))
  }
}

总结

简单使用了页面路由进行跳转、传参、返回、传参等实现了学生页面详情界面的UI,并学习了UIAbility的生命周期状态与UIAbility的启动模式。

为了能让大家更好的学习鸿蒙 (Harmony OS) 开发技术,这边特意整理了《鸿蒙 (Harmony OS)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙 (Harmony OS)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. 应用开发导读(Java)

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

你可能感兴趣的:(OpenHarmony,鸿蒙,移动开发,harmonyos,华为,移动开发,ArkTS,OpenHarmony)