前文提到过,在使用DevEco创建鸿蒙项目的时候,会选择Empty Ability,那么这个Ability是什么呢?其实对比Android Studio创建Android项目时选择的Empty Activity,感觉Harmony的Ability更像是Android的Activity,但只能说像,不完全等同。本篇,我们就基于API9一起探索下UIAbility。因为本人是Android开发者,所以文章中会时不时的跟Android对比。
鸿蒙系列上一篇
目录
3、onWindowStageCreate和onWindowStageDestroy
一、UIAbility概述
在介绍UIAbility之前,我们先看下Ability,Ability直译过来就是能力。什么能力呢?引用官方开发者社区对Ability的解释:
Ability是应用/服务所具备的能力的抽象,一个Module可以包含一个或多个Ability。应用/服务先后提供了两种应用模型:
可以看到,Ability是应用/服务所具备的能力的抽象。Stage模型的Ability是主流,因此,本篇我们也基于Stage模型下,对UIAbility进行讲解。
UIAbility组件是一种包含UI界面的应用组件,主要用于和用户交互。UIAbility组件是系统调度的基本单元,为应用提供绘制界面的窗口。一个UIAbility组件中可以通过多个页面来实现一个功能模块。
需要注意的是:
每一个UIAbility组件实例,都对应于一个最近任务列表中的任务。
因此,对于开发者而言,可以根据具体的场景选择单个还是多个UIAbility。
二、UIAbility生命周期
上面也多次提到过,UIAbility组件是一种包含UI界面的应用组件,主要用于和用户交互。既然是包含了跟用户交互的UI界面的应用组件,那么肯定就离不开生命周期。即UIAbility从创建到跟用户交互到最终销毁的整个过程。接下来探索下UIAbility的生命周期。
1、生命周期概述
当用户打开、切换和返回到对应应用时,应用中的UIAbility实例会在其生命周期的不同状态之间转换。UIAbility类提供了一系列回调,通过这些回调可以知道当前UIAbility实例的某个状态发生改变,会经过UIAbility实例的创建和销毁,或者UIAbility实例发生了前后台的状态切换。
UIAbility的生命周期包括onCreate、onForeground、onBackground、onDestroy四个状态,如下图所示:
2、onCreate
onCreate状态为在应用加载过程中,UIAbility实例创建完成时触发,系统会调用onCreate()回调。可以在该回调中进行应用初始化操作,例如变量定义资源加载等,用于后续的UI界面展示。
- import UIAbility from '@ohos.app.ability.UIAbility';
- import Window from '@ohos.window';
-
- export default class EntryAbility extends UIAbility {
- onCreate(want, launchParam) {
- // 应用初始化
- }
- // ...
- }
3、onWindowStageCreate和onWindowStageDestroy
UIAbility实例创建完成之后,在进入Foreground之前,系统会创建一个WindowStage。WindowStage创建完成后会进入onWindowStageCreate()回调,可以在该回调中通过loadContent()方法设置应用要加载的页面并根据需要订阅WindowStage的事件(获焦/失焦、可见/不可见)。
- import UIAbility from '@ohos.app.ability.UIAbility';
- import Window from '@ohos.window';
-
- export default class EntryAbility extends UIAbility {
- onWindowStageCreate(windowStage: Window.WindowStage) {
- // 设置WindowStage的事件订阅(获焦/失焦、可见/不可见)
- try {
- windowStage.on('windowStageEvent', (data) => {
- let stageEventType: window.WindowStageEventType = data;
- switch (stageEventType) {
- case window.WindowStageEventType.SHOWN: // 可见
- hilog.info(0x0000, this.tag, 'windowStage SHOWN.');
- break;
- case window.WindowStageEventType.ACTIVE: // 获焦
- hilog.info(0x0000, this.tag, 'windowStage ACTIVE.');
- break;
- case window.WindowStageEventType.INACTIVE: // 失焦
- hilog.info(0x0000, this.tag, 'windowStage INACTIVE.');
- break;
- case window.WindowStageEventType.HIDDEN: // 不可见
- hilog.info(0x0000, this.tag, 'windowStage HIDDEN.');
- break;
- default:
- break;
- }
- });
- } catch (exception) {
- hilog.error(0x000, this.tag, 'Failed to enable the listener for window stage event changes. Cause:' +
- JSON.stringify(exception));
- }
- // 设置UI界面加载
- windowStage.loadContent('pages/Index', (err, data) => {
- // ...
- });
- }
- }
同样的,在UIAbility实例销毁之前,则会先进入onWindowStageDestroy()回调,可以在该回调中释放UI界面资源。例如在onWindowStageDestroy()中注销获焦/失焦等WindowStage事件。
- import UIAbility from '@ohos.app.ability.UIAbility';
- import Window from '@ohos.window';
-
- export default class EntryAbility extends UIAbility {
- // ...
-
- onWindowStageDestroy() {
- // 释放UI界面资源
- }
- }
4、 onForeground和onBackground
onForeground()回调,在UIAbility实例切换至前台时触发,在UIAbility的UI界面可见之前调用。onBackground()回调,在UIAbility实例切换至后台时候触发,在UIAbility的UI界面完全不可见之后调用。
- import UIAbility from '@ohos.app.ability.UIAbility';
-
- export default class EntryAbility extends UIAbility {
- onForeground() {
- // 申请系统需要的资源,或者重新申请在onBackground中释放的资源
- }
-
- onBackground() {
- // 释放UI界面不可见时无用的资源,或者在此回调中执行较为耗时的操作
- // 例如状态保存等
- }
- }
5、 onDestroy
onDestroy状态在UIAbility实例销毁时触发。可以在onDestroy()回调中进行系统资源的释放、数据的保存等操作。例如调用terminateSelf()方法停止当前UIAbility实例,从而完成UIAbility实例的销毁;或者用户使用最近任务列表关闭该UIAbility实例,完成UIAbility的销毁。
- import UIAbility from '@ohos.app.ability.UIAbility';
- import Window from '@ohos.window';
-
- export default class EntryAbility extends UIAbility {
- onDestroy() {
- // 系统资源的释放、数据的保存等
- }
- }
三、UIAbility启动模式
同Android的Activity一样,UIAbility也有启动模式。启动模式是指UIAbility实例在启动时的不同呈现状态。针对不同的业务场景,系统提供了三种启动模式:
- singleton启动模式(单实例模式)
- standard启动模式(标准实例模式)
- specified启动模式(指定实例模式)
1、 singleton启动模式
singleton启动模式为单实例模式,也是默认情况下的启动模式。每次调用startAbility()方法时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例。系统中只存在唯一一个该UIAbility实例,即在最近任务列表中只存在一个该类型的UIAbility实例。
应用的UIAbility实例已创建,该UIAbility配置为单实例模式,再次调用startAbility()方法启动该UIAbility实例,此时只会进入该UIAbility的onNewWant回调,不会进入其onCreate()和onWindowStageCreate()生命周期回调。
如果需要配置单实例模式,module.json5中的"launchType"字段配置为"singleton"即可。
2、 standard启动模式
standard启动模式为标准实例模式,每次调用startAbility()方法时,都会在应用进程中创建一个新的该类型UIAbility实例。即在最近任务列表中可以看到有多个该类型的UIAbility实例。这种情况下可以将UIAbility配置为standard(标准实例模式)。
如果需要配置单实例模式,module.json5中的"launchType"字段配置为"standard"即可。
3、 specified启动模式
specified启动模式为指定实例模式,针对一些特殊场景使用。在UIAbility实例创建之前,允许开发者为该实例创建一个唯一的字符串Key,创建的UIAbility实例绑定Key之后,后续每次调用startAbility()方法时,都会询问应用使用哪个Key对应的UIAbility实例来响应startAbility()请求。运行时由UIAbility内部业务决定是否创建多实例,如果匹配有该UIAbility实例的Key,则直接拉起与之绑定的UIAbility实例,否则创建一个新的UIAbility实例。
四、UIAbility组件间交互
当一个应用内包含多个UIAbility时,存在应用内启动UIAbility的场景。那么,如何在一个UIAbility中启动另一个UIAbility呢?
假设应用中有两个UIAbility:EntryAbility和FuncAbility(可以在应用的一个Module中,也可以在的不同Module中),需要从EntryAbility的页面中启动FuncAbility。
注意:我们暂且只在EntryAbility可见时去跳转FuncAbility,不在页面控件的点击事件中去触发。页面和UIAbility之间的交互,在后续的文章中会讲到。
1、创建UIAbility
创建新的UIAbility步骤如下:New -> Ability,跟Android的Activity类似,同时需要在对应的配置文件中进行注册。Android是AndroidManifest.xml文件,而harmony则是module.json5文件。如下图所示,是我创建的一个Demo,默认的EntryAbility继承自UIAbility,声明信息如下:
UIAbility部分声明信息详解(大部分标签不用多解释,跟Android类似):
- {
- "module": {
- // ...
- "abilities": [
- {
- "name": "EntryAbility", // UIAbility组件的名称
- "srcEntry": "./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组件启动页面背景颜色资源文件的索引
- "exported": true, // 标识当前UIAbility组件是否可以被其他应用调用
- "skills": [ // 标识当前Ability组件能够接收的Want的特征集,为数组格式[类似安卓的IntentFilter]。
- {
- "entities": [
- "entity.system.home"
- ],
- "actions": [
- "action.system.home"
- ]
- }
- ]
- }
- ]
- }
- }
更完整的声明信息见module.json5配置文件
2、创建页面
仅UIAbility也可以,但是没有可展示的页面打开后是空白的,有点奇奇怪怪。所以,我们给新建的FuncUIAbility创建一个页面Func,代码很简单,直接赋值Index.ets,或者New - Page,修改一下message用来区分:
3、绑定UIAbility和页面
这个在讲生命周期的时候说过了,在onWindowStageCreate中调用windowStage.loadContent即可:
- onWindowStageCreate(windowStage: window.WindowStage) {
- // Main window is created, set main page for this ability
- hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
-
- windowStage.loadContent('pages/Func', (err, data) => {
- if (err.code) {
- hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
- return;
- }
- hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
- });
- }
注意:如果是直接赋值的Page,需要在resources/profile/main_pages.json中添加新创建的page,,否则UIAbility中无法展示该页面!!!如果New - Page的方式创建,则自动更新,无需关注。
如下图所示:
4、启动UIAbility
在EntryUIAbility加载完页面时,也即onWindowStageCreate中调用windowStage.loadContent
后,去触发跳转FuncUIAbility,代码如下:
- openFuncUiAbility(){
- // 调起app内其他的UIAbility
- let want: Want = {
- deviceId: '', // deviceId为空表示本设备
- bundleName: 'com.example.tuduharmonydemo', // 必填
- moduleName: '', // moduleName为空表示本模块
- abilityName: 'FuncAbility', // 必填
- parameters: { // 自定义信息
- info: '来自EntryAbility Index页面',
- },
- }
- this.context.startAbility(want).then(() => {
- hilog.info(0x0000, this.tag, 'Succeeded in starting ability.');
- }).catch((err) => {
- hilog.error(0x0000, this.tag, `Failed to start ability. Code is ${err.code}, message is ${err.message}`);
- })
- }
初学的同学可能不清楚bundleName应该怎么填写,这里特别讲解下。bundleName,在AppScope/resources/app.json5中描述的。如下图所示:
看下录屏的效果:
本篇介绍了鸿蒙开发的重要组件UIAbility,介绍了UIAbility是什么,并且围绕UIAbility的生命周期和三种启动模式进行了详细的阐述。既然是组件,固然离不开组件之间的交互。因此,在最后讲解了UIAbility组件之间的交互。下一篇,我们一起学习下UIAbility和Page之间的交互。
评论记录:
回复评论: