写点什么

HarmonyOS 学习路之开发篇——Page Ability

发布于: 2021 年 06 月 15 日

Page Ability

Page Ability 基本概念

Page 与 AbilitySlice

Page 模板(以下简称“Page”)是 FA 唯一支持的模板,用于提供与用户交互的能力。一个 Page 可以由一个或多个 AbilitySlice 构成,AbilitySlice 是指应用的单个页面及其控制逻辑的总和。


当一个 Page 由多个 AbilitySlice 共同构成时,这些 AbilitySlice 页面提供的业务能力应具有高度相关性。例如,新闻浏览功能可以通过一个 Page 来实现,其中包含了两个 AbilitySlice:一个 AbilitySlice 用于展示新闻列表,另一个 AbilitySlice 用于展示新闻详情。Page 和 AbilitySlice 的关系如图 1 所示。图 1 Page 与 AbilitySlice



相比于桌面场景,移动场景下应用之间的交互更为频繁。通常,单个应用专注于某个方面的能力开发,当它需要其他能力辅助时,会调用其他应用提供的能力。例如,外卖应用提供了联系商家的业务功能入口,当用户在使用该功能时,会跳转到通话应用的拨号页面。与此类似,HarmonyOS 支持不同 Page 之间的跳转,并可以指定跳转到目标 Page 中某个具体的 AbilitySlice。

AbilitySlice 路由配置

虽然一个 Page 可以包含多个 AbilitySlice,但是 Page 进入前台时界面默认只展示一个 AbilitySlice。默认展示的 AbilitySlice 是通过 setMainRoute()方法来指定的。如果需要更改默认展示的 AbilitySlice,可以通过 addActionRoute()方法为此 AbilitySlice 配置一条路由规则。此时,当其他 Page 实例期望导航到此 AbilitySlice 时,可以在 Intent 中指定 Action,详见不同 Page 间导航。


setMainRoute()方法与 addActionRoute()方法的使用示例如下:


public class MyAbility extends Ability {    @Override    public void onStart(Intent intent) {        super.onStart(intent);        // set the main route        setMainRoute(MainSlice.class.getName());
// set the action route addActionRoute("action.pay", PaySlice.class.getName()); addActionRoute("action.scan", ScanSlice.class.getName()); }}
复制代码


addActionRoute()方法中使用的动作命名,需要在应用配置文件(config.json)中注册:


{    "module": {        "abilities": [            {                "skills":[                    {                        "actions":[                            "action.pay",                            "action.scan"                        ]                    }                ]                ...            }        ]        ...    }    ...}
复制代码

Page Ability 生命周期

系统管理或用户操作等行为均会引起 Page 实例在其生命周期的不同状态之间进行转换。Ability 类提供的回调机制能够让 Page 及时感知外界变化,从而正确地应对状态变化(比如释放资源),这有助于提升应用的性能和稳健性。

Page 生命周期回调

Page 生命周期的不同状态转换及其对应的回调,如图 1 所示。图 1 Page 生命周期



  • onStart()当系统首次创建 Page 实例时,触发该回调。对于一个 Page 实例,该回调在其生命周期过程中仅触发一次,Page 在该逻辑后将进入 INACTIVE 状态。开发者必须重写该方法,并在此配置默认展示的 AbilitySlice。


 @Override    public void onStart(Intent intent) {        super.onStart(intent);        super.setMainRoute(FooSlice.class.getName());    }
复制代码


  • onActive()Page 会在进入 INACTIVE 状态后来到前台,然后系统调用此回调。Page 在此之后进入 ACTIVE 状态,该状态是应用与用户交互的状态。Page 将保持在此状态,除非某类事件发生导致 Page 失去焦点,比如用户点击返回键或导航到其他 Page。当此类事件发生时,会触发 Page 回到 INACTIVE 状态,系统将调用 onInactive()回调。此后,Page 可能重新回到 ACTIVE 状态,系统将再次调用 onActive()回调。因此,开发者通常需要成对实现 onActive()和 onInactive(),并在 onActive()中获取在 onInactive()中被释放的资源。

  • onInactive()当 Page 失去焦点时,系统将调用此回调,此后 Page 进入 INACTIVE 状态。开发者可以在此回调中实现 Page 失去焦点时应表现的恰当行为。

  • onBackground()如果 Page 不再对用户可见,系统将调用此回调通知开发者用户进行相应的资源释放,此后 Page 进入 BACKGROUND 状态。开发者应该在此回调中释放 Page 不可见时无用的资源,或在此回调中执行较为耗时的状态保存操作。

  • onForeground()处于 BACKGROUND 状态的 Page 仍然驻留在内存中,当重新回到前台时(比如用户重新导航到此 Page),系统将先调用 onForeground()回调通知开发者,而后 Page 的生命周期状态回到 INACTIVE 状态。开发者应当在此回调中重新申请在 onBackground()中释放的资源,最后 Page 的生命周期状态进一步回到 ACTIVE 状态,系统将通过 onActive()回调通知开发者用户。

  • onStop()

  • 系统将要销毁 Page 时,将会触发此回调函数,通知用户进行系统资源的释放。销毁 Page 的可能原因包括以下几个方面:

  • 用户通过系统管理能力关闭指定 Page,例如使用任务管理器关闭 Page。

  • 用户行为触发 Page 的 terminateAbility()方法调用,例如使用应用的退出功能。 配置变更导致系统暂时销毁 Page 并重建。

  • 系统出于资源管理目的,自动触发对处于 BACKGROUND 状态 Page 的销毁。

AbilitySlice 生命周期

AbilitySlice 作为 Page 的组成单元,其生命周期是依托于其所属 Page 生命周期的。AbilitySlice 和 Page 具有相同的生命周期状态和同名的回调,当 Page 生命周期发生变化时,它的 AbilitySlice 也会发生相同的生命周期变化。此外,AbilitySlice 还具有独立于 Page 的生命周期变化,这发生在同一 Page 中的 AbilitySlice 之间导航时,此时 Page 的生命周期状态不会改变。


AbilitySlice 生命周期回调与 Page 的相应回调类似,因此不再赘述。由于 AbilitySlice 承载具体的页面,开发者必须重写 AbilitySlice 的 onStart()回调,并在此方法中通过 setUIContent()方法设置页面,如下所示:


@Overrideprotected void onStart(Intent intent) {    super.onStart(intent);
setUIContent(ResourceTable.Layout_main_layout);}
复制代码


AbilitySlice 实例创建和管理通常由应用负责,系统仅在特定情况下会创建 AbilitySlice 实例。例如,通过导航启动某个 AbilitySlice 时,是由系统负责实例化;但是在同一个 Page 中不同的 AbilitySlice 间导航时则由应用负责实例化。

Page 与 AbilitySlice 生命周期关联

当 AbilitySlice 处于前台且具有焦点时,其生命周期状态随着所属 Page 的生命周期状态的变化而变化。当一个 Page 拥有多个 AbilitySlice 时,例如:MyAbility 下有 FooAbilitySlice 和 BarAbilitySlice,当前 FooAbilitySlice 处于前台并获得焦点,并即将导航到 BarAbilitySlice,在此期间的生命周期状态变化顺序为:FooAbilitySlice 从 ACTIVE 状态变为 INACTIVE 状态。BarAbilitySlice 则从 INITIAL 状态首先变为 INACTIVE 状态,然后变为 ACTIVE 状态(假定此前 BarAbilitySlice 未曾启动)。FooAbilitySlice 从 INACTIVE 状态变为 BACKGROUND 状态。对应两个 slice 的生命周期方法回调顺序为:


FooAbilitySlice.onInactive() --> BarAbilitySlice.onStart() --> BarAbilitySlice.onActive() --> FooAbilitySlice.onBackground()


在整个流程中,MyAbility 始终处于 ACTIVE 状态。但是,当 Page 被系统销毁时,其所有已实例化的 AbilitySlice 将联动销毁,而不仅是处于前台的 AbilitySlice。

AbilitySlice 间导航

同一 Page 内导航

当发起导航的 AbilitySlice 和导航目标的 AbilitySlice 处于同一个 Page 时,您可以通过 present()方法实现导航。如下代码片段展示通过点击按钮导航到其他 AbilitySlice 的方法:


@Overrideprotected void onStart(Intent intent) {
... Button button = ...; button.setClickedListener(listener -> present(new TargetSlice(), new Intent())); ...
}
复制代码


如果开发者希望在用户从导航目标 AbilitySlice 返回时,能够获得其返回结果,则应当使用 presentForResult()实现导航。用户从导航目标 AbilitySlice 返回时,系统将回调 onResult()来接收和处理返回结果,开发者需要重写该方法。返回结果由导航目标 AbilitySlice 在其生命周期内通过 setResult()进行设置。


@Overrideprotected void onStart(Intent intent) {
... Button button = ...; button.setClickedListener(listener -> presentForResult(new TargetSlice(), new Intent(), 0)); ...
}
@Overrideprotected void onResult(int requestCode, Intent resultIntent) { if (requestCode == 0) { // Process resultIntent here. }}
复制代码


系统为每个 Page 维护了一个 AbilitySlice 实例的栈,每个进入前台的 AbilitySlice 实例均会入栈。当开发者在调用 present()或 presentForResult()时指定的 AbilitySlice 实例已经在栈中存在时,则栈中位于此实例之上的 AbilitySlice 均会出栈并终止其生命周期。前面的示例代码中,导航时指定的 AbilitySlice 实例均是新建的,即便重复执行此代码(此时作为导航目标的这些实例是同一个类),也不会导致任何 AbilitySlice 出栈。

不同 Page 间导航

AbilitySlice 作为 Page 的内部单元,以 Action 的形式对外暴露,因此可以通过配置 Intent 的 Action 导航到目标 AbilitySlice。Page 间的导航可以使用 startAbility()或 startAbilityForResult()方法,获得返回结果的回调为 onAbilityResult()。在 Ability 中调用 setResult()可以设置返回结果。详细用法可参考根据 Operation 的其他属性启动应用中的示例。

跨设备迁移

跨设备迁移(下文简称“迁移”)支持将 Page 在同一用户的不同设备间迁移,以便支持用户无缝切换的诉求。以 Page 从设备 A 迁移到设备 B 为例,迁移动作主要步骤如下:


  1. 设备 A 上的 Page 请求迁移。

  2. HarmonyOS 处理迁移任务,并回调设备 A 上 Page 的保存数据方法,用于保存迁移必须的数据。

  3. HarmonyOS 在设备 B 上启动同一个 Page,并回调其恢复数据方法。 开发者可以参考以下详细步骤开发具有迁移功能的 Page。

实现 IAbilityContinuation 接口

说明一个应用可能包含多个 Page,仅需要在支持迁移的 Page 中通过以下方法实现 IAbilityContinuation 接口。同时,此 Page 所包含的所有 AbilitySlice 也需要实现此接口。


  1. onStartContinuation()Page 请求迁移后,系统首先回调此方法,开发者可以在此回调中决策当前是否可以执行迁移,比如,弹框让用户确认是否开始迁移。

  2. onSaveData()如果 onStartContinuation()返回 true,则系统回调此方法,开发者在此回调中保存必须传递到另外设备上以便恢复 Page 状态的数据。

  3. onRestoreData()源侧设备上 Page 完成保存数据后,系统在目标侧设备上回调此方法,开发者在此回调中接受用于恢复 Page 状态的数据。注意,在目标侧设备上的 Page 会重新启动其生命周期,无论其启动模式如何配置。且系统回调此方法的时机在 onStart()之前。

  4. onCompleteContinuation()目标侧设备上恢复数据一旦完成,系统就会在源侧设备上回调 Page 的此方法,以便通知应用迁移流程已结束。开发者可以在此检查迁移结果是否成功,并在此处理迁移结束的动作,例如,应用可以在迁移完成后终止自身生命周期。

  5. onRemoteTerminated()如果开发者使用 continueAbilityReversibly()而不是 continueAbility(),则此后可以在源侧设备上使用 reverseContinueAbility()进行回迁。这种场景下,相当于同一个 Page(的两个实例)同时在两个设备上运行,迁移完成后,如果目标侧设备上 Page 因任何原因终止,则源侧 Page 通过此回调接收终止通知。

请求迁移

实现 IAbilityContinuation 的 Page 可以在其生命周期内,调用 continueAbility()或 continueAbilityReversibly()请求迁移。两者的区别是,通过后者发起的迁移此后可以进行回迁。


try {    continueAbility();} catch (IllegalStateException e) {    // Maybe another continuation in progress.    ...}
复制代码


以 Page 从设备 A 迁移到设备 B 为例,详细的流程如下:


  1. 设备 A 上的 Page 请求迁移。

  2. 系统回调设备 A 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onStartContinuation()方法,以确认当前是否可以立即迁移。

  3. 如果可以立即迁移,则系统回调设备 A 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onSaveData()方法,以便保存迁移后恢复状态必须的数据。

  4. 如果保存数据成功,则系统在设备 B 上启动同一个 Page,并恢复 AbilitySlice 栈,然后回调 IAbilityContinuation.onRestoreData()方法,传递此前保存的数据;此后设备 B 上此 Page 从 onStart()开始其生命周期回调。

  5. 系统回调设备 A 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onCompleteContinuation()方法,通知数据恢复成功与否。

请求回迁

使用 continueAbilityReversibly()请求迁移并完成后,源侧设备上已迁移的 Page 可以发起回迁,以便使用户活动重新回到此设备。


try {    reverseContinueAbility();} catch (IllegalStateException e) {    // Maybe another continuation in progress.    ...}
复制代码


以 Page 从设备 A 迁移到设备 B 后并请求回迁为例,详细的流程如下:


  1. 设备 A 上的 Page 请求回迁。

  2. 系统回调设备 B 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onStartContinuation()方法,以确认当前是否可以立即迁移。

  3. 如果可以立即迁移,则系统回调设备 B 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onSaveData()方法,以便保存回迁后恢复状态必须的数据。

  4. 如果保存数据成功,则系统在设备 A 上 Page 恢复 AbilitySlice 栈,然后回调 IAbilityContinuation.onRestoreData()方法,传递此前保存的数据。

  5. 如果数据恢复成功,则系统终止设备 B 上 Page 的生命周期。

发布于: 2021 年 06 月 15 日阅读数: 372
用户头像

公众号【美男子玩编程】 2020.05.07 加入

精通移动开发、Android开发; 熟练应用java/JavaScript进行HarmonyOS开发; 熟练使用HTML/CSS语言进行网页开发。

评论

发布
暂无评论
HarmonyOS学习路之开发篇——Page Ability