5 Page Ability 本章学习目标 .熟练掌握Ability的架构。 .了解PageAbility基础知识。 .熟练掌握PageAbility生命周期。 .掌握三种页面跳转实现方法。 ..5.1 Ability概述 Ability是HarmonyOS 应用程序的核心组成部分,分为FA(FeatureAbility)和PA (ParticleAbility)两种类型,PageAbility是FA 唯一支持的Ability,用于提供与用户交互 的能力。本章先介绍Ability的基础知识,再重点学习PageAbility的生命周期以及各种页 面跳转的使用方法。 在HarmonyOS 应用中,有一个非常核心的概念,那就是Ability(能力)。Ability是应 用所具备能力的抽象,也是应用程序的重要组成部分。一个应用可以具备多种Ability(可 以包含PageAbility、ServiceAbility、DataAbility),HarmonyOS 支持应用以Ability为单 位进行部署。 Ability可以分为FA(FeatureAbility)和PA(ParticleAbility)两种类型,每种类型为 开发者提供了不同的模板,以便实现不同的业务功能。其中,FA 有UI 界面,提供与用户交 互的能力;而PA 无UI 界面,提供后台运行任务以及访问文件或数据库的能力。 FA 唯一支持模板是PageAbility,PA 则包括ServiceAbility和DataAbility。Ability 之间的关系如图5-1所示。 图5- 1 Ability之间的关系 基于FA 或PA 开发的应用能够实现特定的业务功能,支持跨设备调度与分发,为用户 第5章 PageAbility 1 17 提供一致、高效的应用体验。例如,在DevEcoStudio中创建第一个应用时,默认选择的是 一个空的FA 模板,即EmptyAbility,如图5-2所示。 图5-2 选择空的FA 模板 ..5.2 Ability的配置 在配置文件(config.json)中注册Ability时,可以通过配置Ability元素中的type属性 来指定Ability模板类型。 其中,type的取值可以为page、service或data,它们分别代表Page模板、Service模板、 Data模板。为了便于表述,后文中将基于Page模板、Service模板、Data模板实现的Ability 分别简称为Page、Service、Data。 下面创建一个名为PageAbilityLifeCycle的应用,来说明Page中的各个配置选项以及 Page的生命周期。在其Project窗口中打开entry/src/main/config.json文件,在abilities 配置选项中包含了各个Ability的配置选项,内容如下: . "abilities": [ { "skills": [ { "entities": [ "entity.system.home" ], 1 18 鸿蒙应用程序开发 "actions": [ "action.system.home" ] } ], "name": "com.example.pageabilitylifecycle.MainAbility", "description": "$string:mainability_description", "icon": "$media:icon", "label": "$string:entry_MainAbility", "launchType": "standard", "orientation": "unspecified", "visible": true, "type": "page" } ] . 在新创建PageAbilityLifeCycle应用中,abilities配置选项仅有一个Ability对象,类型 是Page。 Ability对象中各个属性的含义如表5-1所示。 表5-1 Ability对象中各属性的含义 属性名称属性含义数据类型是否可缺省 name Ability的名称,采用“包名+类名”的方式定义字符串 否 description Ability的描述字符串 可缺省 icon Ability图标资源文件的索引字符串 可缺省 label Ability对用户显示的名称,默认显示在手机应用程序的标题栏字符串 可缺省 launchType Ability的启动模式,支持standard、singleMission和singleton三种 模式 字符串 可缺省 orientation Ability的显示模式,仅适用于pageAbility。取值范围:unspecified,表 示系统自动判断显示方向;landscape,表示横屏;portrait,表示竖屏; followRecent,表示跟随栈中最近的应用 字符串 可缺省 visible Ability是否可以被其他应用调用。true表示可以,false表示不 可以 布尔类型可缺省 type Ability的类型,取值可为page、service、data 字符串 否 ..5.3 应用分层 5.3.1 应用的三层架构 目前,比较常用的、典型的应用软件倾向使用三层架构(Three-TierArchitecture),包 119 第5章PageAbility 括以下3层。 (1)表示层(PresentationLayer):提供与用户交互的界面。GUI(GraphicalUser Interface,图形用户界面)和Web页面是表示层的两个典型的例子。 (2)业务层(BusinesLayer):也称为业务逻辑层,用于实现各种业务逻辑,例如处理数 据验证根据特定的业务规则和任务来响应特定的行为。 (3)数据访问层(DataAcesLayer):也称为数据持久层,负责存放和管理应用的持久 性业务数据。 三层架构如图5-3所示。 图5- 3 三层架构图 5.3.2 Ability的三层架构 从应用的三层架构中,很容易识别出,Ability同样遵循图5-3所示的三层架构。其中 PageAbility代表表示层,ServiceAbility代表业务层,DataAbility代表数据访问层。 因此,读者在设计Ability时,应先考虑这个Ability需要完成什么样的功能,代表了哪 个层次的业务。 ..5.aeAblty简介 4 Pgii Page模板是FA唯一支持的模板,用于提供与用户交互的能力。一个Page实例可以 包含一组相关页面,每个页面用一个AbilitySlice实例表示,AbilitySlice指应用的单个页面 及其控制逻辑的总和。简而言之,FA承担的就是前端与用户之间的交互工作。 当一个Page由多个AbilitySlice共同构成时,这些 AbilitySlice页面提供的业务能力应具有高度相关性。例如,聊天 软件可以通过一个Page来实现,其中包含了两个AbilitySlice:一 个AbiiySie用于构成联系人列表, iiySie用于和 ltlc另一个Abltlc 具体联系人聊天。Page和AbilitySlice的关系如图5-4所示。 相比于桌面场景,移动场景下应用之间的交互更为频繁。 通常,单个应用专注某个方面的能力开发,当它需要其他能力辅 助时,会调用其他应用提供的能力。例如,在聊天软件中提供了 打开网络链接功能的入口,当用户点击链接时,会打开浏览器, 访问网络链接。与此类似,HarmonyOS支持不同Page之间的 跳转,并可以指定跳转到目标Page中某个具体的AbilitySlice。 图5-4Page和AbilitySlice 的关系 1 20 鸿蒙应用程序开发 ..5.5 生命周期 系统管理或用户操作等行为均会引起Page实例在其生命周期的不同状态之间进行转 换。Ability类提供的回调机制能够让Page及时感知外界变化,从而正确地应对状态变化 (如释放资源),这有助于提升应用的性能和稳健性。 5.5.1 Page生命周期回调 Page生命周期的不同状态转换及其对应的回调如图5-5所示。 图5-5 Page生命周期 说明:INACTIVE状态是一种短暂存在的状态,可理解为“激活中”。 1.onStart() 当系统首次创建Page实例时,触发该回调。对于一个Page实例,该回调在其生命周期 过程中仅触发一次,Page在该逻辑后将进入INACTIVE状态。开发者必须重写该方法,并 在此配置默认展示的AbilitySlice。示例代码如下: @Override public void onStart(Intent intent) { super.onStart(intent); super.setMainRoute(MainAbilitySlice.class.getName()); 第5章 PageAbility 1 21 } 上述代码中的setMainRoute()方法用来设置该Page的主路由,意思是启动该Page后 默认打开其参数设置的AbilitySlice。这里默认打开MainAbilitySlice。 2.onActive() Page会在进入INACTIVE状态后来到前台,然后系统调用此回调。Page在此之后进 入ACTIVE状态,该状态是应用与用户交互的状态。Page将保持在此状态,除非某类事件 发生导致Page失去焦点,例如用户点击返回键或导航到其他Page。当此类事件发生时,会 触发Page回到INACTIVE状态,系统将调用onInactive()回调。此后,Page可能重新回到 ACTIVE状态,系统将再次调用onActive()回调。因此,开发者通常需要成对实现 onActive()和onInactive(),并在onActive()中获取在onInactive()中被释放的资源。示例 代码如下: @Override public void onActive() { super.onActive(); } 3.onInactive() 当Page失去焦点时,系统将调用此回调,此后Page进入INACTIVE状态。开发者可 以在此回调中实现Page失去焦点时应表现的恰当行为。示例代码如下: @Override protected void onInactive() { super.onInactive(); } 4.onBackground() 如果Page不再对用户可见,系统将调用此回调通知开发者用户进行相应的资源释放, 此后Page进入BACKGROUND状态。开发者应该在此回调中释放Page不可见时无用的 资源,或在此回调中执行较为耗时的状态保存操作。示例代码如下: @Override protected void onBackground() { super.onBackground(); } 5.onForeground() 处于BACKGROUND状态的Page仍然驻留在内存中,当重新回到前台时(如用户重 1 22 鸿蒙应用程序开发 新导航到此Page),系统将先调用onForeground()回调通知开发者,而后Page的生命周期 状态回到INACTIVE状态。开发者应当在此回调中重新申请在onBackground()中释放的 资源,最后Page的生命周期状态进一步回到ACTIVE状态,系统将通过onActive()回调通 知开发者用户。示例代码如下: @Override public void onForeground(Intent intent) { super.onForeground(intent); } 6.onStop() 系统要销毁Page时,将会触发此回调函数,通知用户进行系统资源的释放。销毁Page 的可能原因包括以下几个方面。 (1)用户通过系统管理能力关闭指定Page,例如使用任务管理器关闭Page。 (2)用户行为触发Page的terminateAbility()方法调用,例如使用应用的退出功能。 (3)配置变更导致系统暂时销毁Page并重建。 (4)系统出于资源管理目的,自动触发对处于BACKGROUND状态Page的销毁。 示例代码如下: @Override protected void onStop() { super.onStop(); } 5.5.2 AbilitySlice生命周期 AbilitySlice作为Page的组成单元,其生命周期是依托其所属Page生命周期的。 AbilitySlice和Page具有相同的生命周期状态和同名的回调,当Page生命周期发生变化 时,它的AbilitySlice也会发生相同的生命周期变化。此外,AbilitySlice还具有独立于Page 的生命周期变化,这发生在同一Page中的AbilitySlice之间导航时,此时Page的生命周期 状态不会改变。 AbilitySlice生命周期回调与Page的相应回调类似,因此不再赘述。由于AbilitySlice 承载具体的页面,开发者必须重写AbilitySlice的onStart()回调方法,并在此方法中通过 setUIContent()方法设置页面,如下所示: @Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); } 第5章 PageAbility 1 23 上述代码在onStart()方法中调用setUIContent()方法设置页面效果,这里通过XML 文件构建页面内容,其参数是一个XML文件对象,对应的XML文件是ability_main.xml。 AbilitySlice实例创建和管理通常由应用负责,系统仅在特定情况下会创建AbilitySlice 实例。例如,通过导航启动某个AbilitySlice时,是由系统负责实例化;但是在同一个Page 中不同的AbilitySlice间导航时则由应用负责实例化。 5.5.3 案例:Page的生命周期 下面创建一个名为PageAbilityLifeCycle的应用,来演示Page和AbilitySlice的生命周 期。修改ability_main.xml,其内容如下: <? xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" ohos:width="match_parent" ohos:alignment="center" ohos:orientation="vertical"> <Text ohos:id="$+id:Text_mainAbilitySlice" ohos:height="match_content" ohos:width="match_content" ohos:background_element="$graphic:background_ability_main" ohos:layout_alignment="horizontal_center" ohos:text="MainAbilitySlice" ohos:text_size="40fp" /> </DirectionalLayout> 修改MainAbility 中的代码,添加onActive()、onInactive ()、onBackground ()、 onForeground()、onStop()生命周期方法,并定义日志标签,在每个生命周期方法中打印 出对应方法名,以此演示Page的生命周期。其内容如下: public class MainAbility extends Ability { //定义日志标签 private static final HiLogLabel LABEL =new HiLogLabel(HiLog.LOG_APP , 0x00922 , "MainAbility"); @Override public void onStart(Intent intent) { super.onStart(intent); 1 24 鸿蒙应用程序开发 super.setMainRoute(MainAbilitySlice.class.getName()); HiLog.info(LABEL, "onStart"); } @Override public void onActive() { super.onActive(); HiLog.info(LABEL, "onActive"); } @Override protected void onInactive() { super.onInactive(); HiLog.info(LABEL, "onInactive"); } @Override protected void onBackground() { super.onBackground(); HiLog.info(LABEL, "onBackground"); } @Override public void onForeground(Intent intent) { super.onForeground(intent); HiLog.info(LABEL, "onForeground"); } @Override protected void onStop() { super.onStop(); HiLog.info(LABEL, "onStop"); } } 代码说明 使用HiLogLabel(inttype,intdomain,Stringtag)定义日志标签,其参数的含义分别 如下。 type:用于指定打印日志的类型,HiLog中当前只提供了一种日志类型,即应用的日志 类型LOG_APP。 domain:用于指定打印日志所对应的业务领域,取值范围为0x0~0xFFFFF,开发者可 以根据需要进行自定义。 tag:用于指定日志标识,可以为任意字符串,建议标识调用所在的类,这里的日志标识 为MainAbility。 第5章 PageAbility 1 25 开发者可以根据自定义参数domain和tag来进行日志的筛选和查找。 在onStart()方法中使用HiLog.info(LABEL,"onStart");语句,意思是将此日志级别 定义为info。 在MainAbilitySlice中,重复上述操作,用日志打印方法名,演示MainAbilitySlice的生 命周期。其内容如下: public class MainAbilitySlice extends AbilitySlice { //定义日志标签 private static final HiLogLabel LABEL =new HiLogLabel(HiLog.LOG_APP , 0x00922 , "MainAbilitySlice"); @Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); HiLog.info(LABEL, "onStart"); } @Override public void onActive() { super.onActive(); HiLog.info(LABEL, "onActive"); } @Override protected void onInactive() { super.onInactive(); HiLog.info(LABEL, "onInactive"); } @Override protected void onBackground() { super.onBackground(); HiLog.info(LABEL, "onBackground"); } @Override public void onForeground(Intent intent) { super.onForeground(intent); HiLog.info(LABEL, "onForeground"); } @Override