【鸿蒙开发】——一篇读懂鸿蒙开发中最重要的概念:UIAbility

前言

不久前华为已经宣布全新HarmonyOS NEXT 鸿蒙星河版将在今年秋天正式和消费者见面,并已经面向开发者开放申请。鸿蒙星河版会有更智能、更极致的原生体验,也标志着鸿蒙迈向其发展的第二阶段。

因此,对于鸿蒙生态建设而言,2024年可谓至关重要,而生态建设的前提,就是要有足够的开发人才。与之对应的,今年春招市场上与鸿蒙相关岗位和人才旺盛的热度,一方面反应了鸿蒙生态的逐渐壮大,另一方面也让人们对鸿蒙下一阶段的发展更具信心。

前端开发者开发鸿蒙移动应用有许多概念需要理解清楚,这对于刚入门的开发者来说有比较大的理解难度。

本文试图从一个概念UIAbility入手,扩展开来,来熟悉鸿蒙开发的各种概念,以便降低入门难度。

什么是UIAbility?

UIAbility 是一种包含用户界面的应用组件,主要用于和用户进行交互。这里有个关键词:应用组件,既然是组件,那么一个APP应用就可以包含多个应用组件。
UIAbility 也是系统调度的单元,为应用提供窗口在其中绘制界面。
每一个 UIAbility 实例,都对应于一个最近任务列表中的任务。
那什么是任务列表呢?
当你同时打开很多软件时,就可以看到一个任务列表,如下图所示:
在这里插入图片描述

一个应用可以有一个 UIAbility,也可以有多个 UIAbility,如下图所示。
在这里插入图片描述

例如,浏览器应用可以通过一个 UIAbility 结合多页面的形式让用户进行的搜索和浏览内容。

而聊天应用增加一个"外卖功能"的场景,则可以将聊天应用中"外卖功能"的内容独立为一个 UIAbility,当用户打开聊天应用的"外卖功能",查看外卖订单详情,此时有新的聊天消息,即可以通过最近任务列表切换回到聊天窗口继续进行聊天对话。

这样做的好处就是把功能解耦,进行一个模块一个模块的管理,这样极大的提高了项目的可维护性。
对于一个 UIAbility 来说,通常是对应于多个页面。建议将一个独立的功能模块放到一个UIAbility中,以多页面的形式呈现。例如新闻应用在浏览内容的时候,可以进行多页面的跳转使用。

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

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

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

打开DevEco Studio,选择一个Empty Ability工程模板,创建一个工程,例如命名为MyApplication。

●在src/main/ets/entryability目录下,初始会生成一个 UIAbility 文件EntryAbility.ts。可以在 EntryAbility.ts 文件中根据业务需要实现 UIAbility 的生命周期回调内容。
●在src/main/ets/pages目录下,会生成一个 Index 页面。这也是基于 UIAbility 实现的应用的入口页面。可以在Index页面中根据业务需要实现入口页面的功能。
●在src/main/ets/pages目录下,右键 New->Page,新建一个Second页面,用于实现页面间的跳转和数据传递。

为了实现页面的跳转和数据传递,需要新建一个页面。在原有 Index 页面的基础上,新建一个页面,例如命名为 Second.ets。

页面间的导航可以通过页面路由 router 模块来实现。页面路由模块根据页面url找到目标页面,从而实现跳转。

通过页面路由模块,可以使用不同的 url 访问不同的页面,包括跳转到 UIAbility 内的指定页面、用UIAbility 内的某个页面替换当前页面、返回上一页面或指定的页面等。

页面跳转

在使用页面路由之前,需要先导入 router 模块,如下代码所示。

import router from '@ohos.router';

页面跳转的几种方式,根据需要选择一种方式跳转即可。
方式一:router.pushUrl()方法
js复制代码router.pushUrl({
  url: 'pages/Second',
  params: {
    src: 'Index页面传来的数据',
  }
}, router.RouterMode.Single)

方式二:router.replaceUrl()方法

router.replaceUrl({
  url: 'pages/Second',
  params: {
    src: 'Index页面传来的数据',
  }
}, router.RouterMode.Single)

这两个函数都可以用来页面跳转,区别在于

●router.pushUrl():会将一个新的页面推到页面栈的顶部,而旧页面依然存在,如果按下返回键或者调用router.back(),旧页面会回到栈顶。
●router.replaceUrl():会把当前旧页面用新页面来代替,旧页面会被销毁,如果按下返回键或者调用router.back(),不会回到旧页面。

API9及以上,两个方法都新增了 mode 参数,可以将mode参数配置为 router.RouterMode.Single 单实例模式和 router.RouterMode.Standard 多实例模式。

router.RouterMode.Standard为跳转的默认模式,可不写,表示每次都新建一个页面实例,即使已经存在相同url页面实例,如果使用router.pushUrl + router.RouterMode.Standard则会不断新增页面实例。很明显,将已经存在的实例重复创建是一件很消耗内存的事情,所以在这种需要再一次打开栈里面已经存在的实例的场景中,我们还是比较推荐使用Single模式。

router.RouterMode.Single则表示单例模式,如果栈里面已经存在该页面实例,在启动它的时候会直接从栈里面回到栈顶。

参数接收

已经实现了页面的跳转,接下来,在Second页面中如何进行自定义参数的接收呢?
可以先看下pushUrl里面第一个参数RouterOption里面都有哪些属性:
在这里插入图片描述

第二个参数 params 就是页面跳转中携带的参数,可以看到是一个Object,所以如果我们想传一个字符串到下一个页面,就不能直接将一个 string 给到 params,得这样做

router.replaceUrl({
  url: 'pages/Second',
  params: {
    src: 'Index页面传来的数据',
  }
}, router.RouterMode.Single)

在params 里面以一个key-value形式传递参数,而在新页面里面,通过传递过来的key把对应值取出来,在下一个页面获取参数的代码是这样写的:

import router from '@ohos.router';

@Entry
@Component
struct Second {
  @State src: string = (router.getParams() as Record<string, string>)['src'];
  // 页面刷新展示
  ...
}

页面返回

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

●调用router.back()返回的目标页面需要在页面栈中存在才能正常跳转。
●例如调用router.pushUrl()方法跳转到Second页面,在Second页面可以通过调用router.back()方法返回到上一个页面。
●例如调用router.clear()方法清空了页面栈中所有历史页面,仅保留当前页面,此时则无法通过调用router.back()方法返回到上一个页面。

注意:如果返回页与指定页面之间存在若干页面,那么指定页面被推到栈顶,返回页与中间的若干页面会被销毁。

返回时添加询问弹窗

主要是在一些重要页面里面,比如支付页面,或者一些信息填写页面里面,用户在未保存或者提交当前页面的信息时就点击了返回按钮,页面中会弹出个询问框来让用户二次确认是否要进行返回操作。

询问框可以是系统弹框,也可以是自定义弹框。

系统弹框可以使用router.showAlertBeforeBackPage去实现,这个函数里面接收的参数为EnableAlertOptions,这个类里面只有一个message属性,用来在弹框上显示文案。

使用方式如下,在router.back()操作之前,调用一下router.showAlertBeforeBackPage,弹框上会有确定和取消两个按钮,点击取消关闭弹窗停留在当前页面,点击确定就执行router.back()操作。
在这里插入图片描述

但是如果想要更改下按钮文案,或者顺序,或者自定义按钮的点击事件,就不能用系统弹框了,得使用自定义询问框。

自定义询问框使用promptAction.showDialog,在showDialog里面接收的参数为showDialogOptions,可以看下这个类里面有哪些属性:
在这里插入图片描述

可以看到比系统弹框那边多了两个属性,能够设置弹框标题的title以及按钮buttons,可以看到buttons是一个Button的数组,最多可以设置三个按钮,注意这个Button并不是我们熟悉的Button组件,它内部只支持自定义文案以及颜色
在这里插入图片描述

可以看到弹框上面就多了一个标题,以及按钮的文案与颜色也变了。
在这里插入图片描述

那么如何设置点击事件呢?

现在两个按钮点了除了关闭按钮之外是没有别的操作的,如果想要添加其他操作,就需要通过then操作符进行,在then里面会拿到一个ShowDialogSuccessResponse,这个类里面只有一个index属性,这个index就表示按钮的下标,可以通过判断下标来给指定按钮添加事件,代码如下:
在这里插入图片描述

UIAbility的生命周期

当用户浏览、切换和返回到对应应用的时候,应用中的 UIAbility 实例会在其生命周期的不同状态之间转换。
UIAbility 类提供了很多回调,通过这些回调可以知晓当前 UIAbility 的某个状态已经发生改变:例如 UIAbility 的创建和销毁,或者 UIAbility 发生了前后台的状态切换。

例如从桌面点击图库应用图标,到启动图库应用,应用的状态经过了从创建到前台展示的状态变化。如下图所示,从桌面点击图库应用图标启动应用。
在这里插入图片描述

回到桌面,从最近任务列表,切换回到图库应用,应用的状态经过了从后台到前台展示的状态变化。如下图所示,从最近任务列表切换回到图库应用。
在这里插入图片描述

在UIAbility的使用过程中,会有多种生命周期状态。掌握UIAbility的生命周期,对于应用的开发非常重要。

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

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

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

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

例如,用户打开电池管理应用,在应用加载过程中,在UI页面可见之前,可以在onCreate回调中读取当前系统的电量情况,用于后续的UI页面展示。

●UIAbility实例创建完成之后,在进入Foreground之前,系统会创建一个WindowStage。每一个UIAbility实例都对应持有一个WindowStage实例。WindowStage为本地窗口管理器,用于管理窗口相关的内容,例如与界面相关的获焦/失焦、可见/不可见。
可以在onWindowStageCreate回调中,设置UI页面加载loadContent、设置WindowStage的事件订阅。

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) => {
            ...
        });
    }
    ...
}

例如,用户打开游戏应用,正在打游戏的时候,有一个消息通知,打开消息,消息会以弹窗的形式弹出在游戏应用的上方,此时,游戏应用就从获焦切换到了失焦状态,消息应用切换到了获焦状态。对于消息应用,在onWindowStageCreate回调中,会触发获焦的事件回调,可以进行设置消息应用的背景颜色、高亮等操作。

●Foreground和Background状态,分别在UIAbility切换至前台或者切换至后台时触发。分别对应于onForeground回调和onBackground回调。

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

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

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

export default class EntryAbility extends UIAbility {
    ...

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

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

例如,用户打开地图应用查看当前地理位置的时候,假设地图应用已获得用户的定位权限授权。在UI页面显示之前,可以在onForeground回调中打开定位功能,从而获取到当前的位置信息。
当地图应用切换到后台状态,可以在onBackground回调中停止定位功能,以节省系统的资源消耗。

●在UIAbility实例销毁之前,则会先进入onWindowStageDestroy回调,我们可以在该回调中释放UI页面资源。

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

export default class EntryAbility extends UIAbility {
    ...

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

例如,在onWindowStageCreate中设置的获焦/失焦等WindowStage订阅事件。

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

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

export default class EntryAbility extends UIAbility {
    ...

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

例如,用户使用应用的程序退出功能,会调用 UIAbilityContext 的 terminalSelf() 方法,从而完成 UIAbility 销毁。或者用户使用最近任务列表关闭该UIAbility实例时,也会完成UIAbility的销毁。

UIAbility的启动模式

对于浏览器或者新闻等应用,用户在打开该应用,并浏览访问相关内容后,回到桌面,再次打开该应用,显示的仍然是用户当前访问的界面。

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

对于文档应用,用户从文档应用中打开一个文档内容,回到文档应用,继续打开同一个文档,希望打开的还是同一个文档内容。

基于以上场景的考虑,UIAbility 当前支持 singleton(单实例模式)、multiton(多实例模式)和specified(指定实例模式)3种启动模式。

●singleton(单实例模式)

singleton启动模式,也是默认情况下的启动模式。
当用户打开浏览器或者新闻等应用,并浏览访问相关内容后,回到桌面,再次打开该应用,显示的仍然是用户当前访问的界面。

这种情况下可以将 UIAbility 配置为 singleton(单实例模式)。每次调用startAbility()方法时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例,系统中只存在唯一一个该UIAbility实例。

即在最近任务列表中只存在一个该类型的UIAbility实例。
singleton启动模式的开发使用,在module.json5文件中的 launchType 字段配置为singleton即可。

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

在这里插入图片描述

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

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

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

在这里插入图片描述

●specified(指定实例模式)
用户打开文档应用,从文档应用中打开一个文档内容,回到文档应用,继续打开同一个文档,希望打开的还是同一个文档内容;以及在文档应用中新建一个新的文档,每次新建文档,希望打开的都是一个新的空白文档内容。

这种情况下可以将UIAbility配置为specified(指定实例模式)。在UIAbility实例新创建之前,允许开发者为该实例创建一个字符串Key,新创建的UIAbility实例绑定Key之后,后续每次调用startAbility方法时,都会询问应用使用哪个Key对应的UIAbility实例来响应startAbility请求。

如果匹配有该UIAbility实例的Key,则直接拉起与之绑定的UIAbility实例,否则创建一个新的UIAbility实例。运行时由UIAbility内部业务决定是否创建多实例。
在这里插入图片描述

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。随着鸿蒙的不断发展以及国家的大力支持,未来鸿蒙职位肯定会迎来一个大的爆发,只有积极应对变化,不断学习和提升自己,我们才能在这个变革的时代中立于不败之地。在这里插入图片描述

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值