鸿蒙跨设备协同开发07——动态控制应用接续

107 阅读4分钟

1、前言

本文的内容是基于鸿蒙跨设备协同开发06——应用接续的进一步讨论。

从API version 10起,提供了支持动态配置迁移能力的功能。即应用可以根据实际使用场景,在需要迁移功能时,设置开启应用迁移能力;在业务不需要迁移时,则可以关闭迁移能力(例如:编辑类应用在编辑文本的页面下才需要迁移,其他页面不需要迁移)。

我们可以通过调用setMissionContinueState接口对迁移能力进行设置。setMissionContinueState接口定义如下:

// callback版本
setMissionContinueState(state: AbilityConstant.ContinueState, callback: AsyncCallback<void>): void

// Promiseb版本
setMissionContinueState(state: AbilityConstant.ContinueState): Promise<void>

其中AbilityConstant.ContinueState是枚举,定义如下:

enum ContinueState {
  CONTINUATION = 0, // 迁移保存状态。
  APP_RECOVERY = 1 // 应用恢复保存状态。
}

以Promise版本为例,设置示例代码如下:

import { UIAbility, AbilityConstant } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

export default class EntryAbility extends UIAbility {
  onForeground() {
    this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE).then(() => {
      console.info('success');
    }).catch((err: BusinessError) => {
      console.error(`setMissionContinueState failed, code is ${err.code}, message is ${err.message}`);
    });
  }
}

2、动态迁移实现

默认状态下,应用的迁移能力为ACTIVE状态(可以迁移)。

如果需要实现某些特殊场景(比如只在具体某个页面下支持迁移,或者只在某个事件发生时才支持迁移)可以按照如下步骤进行配置。

👉🏻 step 1:先默认关闭迁移能力

由于默认情况下,迁移能力是ACTIVE的,因此我们需要如果想默认先关闭迁移能力,就需要在Ability的onCreate生命周期回调中关闭(发生的时机靠前),示例代码如下:

// EntryAbility.ets
import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import Want from '@ohos.app.ability.Want';
export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // ...
    this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE, (result) => {
      console.info(`setMissionContinueState: ${JSON.stringify(result)}`);
    });
    // ...
  }
}

👉🏻 step 2:在需要开启的场景动态打开

如果需要在具体某个页面中打开迁移能力,可以在该页面的onPageShow()函数中设置。代码如下:

// PageName.ets
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import common from '@ohos.app.ability.common'
@Entry
@Component
struct PageName {
  private context = getContext(this) as common.UIAbilityContext;
  build() {
    // ...
  }
  // ...
  onPageShow(){
  // 进入该页面时,将应用设置为可迁移状态
    this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
      console.info('setMissionContinueState ACTIVE result: ', JSON.stringify(result));
    });
  }
}

如果在某个组件的触发事件中打开迁移能力,可以在该事件中设置。代码如下:

// PageName.ets
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import common from '@ohos.app.ability.common'
@Entry
@Component
struct PageName {
  private context = getContext(this) as common.UIAbilityContext;
  build() {
    // ...
    Button() {
      //...
    }.onClick(()=>{
    //点击该按钮时,将应用设置为可迁移状态
      this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
        console.info('setMissionContinueState ACTIVE result: ', JSON.stringify(result));
      });
    })
  }
}

❓ 迁移后的回迁问题

我们现在分析一个场景:

迁移加载时,目标端拉起的应用可能执行过自己的迁移状态设置命令(如:冷启动时目标端在onCreate中设置了INACTIVE;热启动时对端已打开了不可迁移的页面,迁移状态为INACTIVE等情况)

为了保证迁移过后的应用依然具有可以迁移回源端的能力,我们应在onCreate和onNewWant的迁移调用判断中,如果是从迁移状态启动的,则将迁移状态设置为ACTIVE。

【我们在鸿蒙跨设备协同开发06——应用接续已经知道,在迁移状态启动后,我们通过onCreate方法和onNewWant方法中的launchParam中的launchReason参数知道当前应用的启动方式】

因此,我们如果是从源端迁移过来启动的,同时,我们又想要保留迁移回源端的能力。那我们需要在onCreate和onNewWant中针对迁移场景做Active激活操作。示例代码如下:

// EntryAbility.ets
import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import Want from '@ohos.app.ability.Want';
export default class EntryAbility extends UIAbility {
  // ...
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // ...
    // 迁移冷启动时,设置状态为可迁移
    if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
      this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
        console.info(`setMissionContinueState: ${JSON.stringify(result)}`);
      });
    }
  }
  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // ...
    // 迁移热启动时,设置状态为可迁移
    if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
      this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
        console.info('setMissionContinueState ACTIVE result: ', JSON.stringify(result));
      });
    }
  }
  // ...
}

3、页面栈迁移问题

应用接续还支持应用动态选择是否进行页面栈恢复(默认进行页面栈信息恢复)。如果应用不想使用系统默认恢复的页面栈,则可以设置不进行页面栈迁移,而需要在onWindowStageRestore设置迁移后进入的页面。

  • 当前仅支持router路由的页面栈信息自动恢复,暂不支持navigation路由的页面栈自动恢复
  • 如果应用使用navigation路由,可以设置不进行页面栈迁移,并将需要接续的页面(或页面栈)信息保存在want中传递,然后在目标端手动加载指定页面。

举例:假设应用在源端的页面栈中存在Index和Second路由,而在目标端恢复时不需要按照源端页面栈进行恢复,需要恢复到指定页面。代码如下:

// EntryAbility.ets
import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import wantConstant from '@ohos.app.ability.wantConstant';
import window from '@ohos.window';
export default class EntryAbility extends UIAbility {
  // ...
  onContinue(wantParam: Record<string, Object>) {
    console.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`);
    wantParam[wantConstant.Params.SUPPORT_CONTINUE_PAGE_STACK_KEY] = false;
    return AbilityConstant.OnContinueResult.AGREE;
  }
  // ...
  onWindowStageRestore(windowStage: window.WindowStage) {
      // 若不需要自动迁移页面栈信息,则需要在此处设置应用迁移后进入的页面
    windowStage.loadContent('pages/Index', (err, data) => {
      if (err.code) {
        console.info('Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      console.info('Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
    });
  }
}

4、接续后,源端退出控制

支持应用动态选择迁移成功后是否退出迁移源端应用(默认迁移成功后退出迁移源端应用)。

如果应用不想让系统自动退出迁移源端应用,则可以设置不退出。设置方法示例如下:

import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import wantConstant from '@ohos.app.ability.wantConstant';
export default class EntryAbility extends UIAbility {
  // ...
  onContinue(wantParam: Record<string, Object>) {
    console.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`);
    // 设置接续后,源端不退出应用
    wantParam[wantConstant.Params.SUPPORT_CONTINUE_SOURCE_EXIT_KEY] = false;
    return AbilityConstant.OnContinueResult.AGREE;
  }
  // ...
}

5、接续不同的Ability

一般情况下,跨端迁移的双端是同Ability之间,但有些应用在不同设备类型下的同一个业务Ability名称不同(即异Ability)。

为了支持该场景下的两个Ability之间能够完成迁移,可以通过在module.json5文件的abilities标签中配置迁移类型continueType进行关联。需要迁移的两个Ability的continueType字段取值必须保持一致,示例如下:

 // 设备A
   {
     "module": {
       // ...
       "abilities": [
         {
           // ...
           "name": "Ability-deviceA",
           "continueType": ['continueType1'], // continueType标签配置
         }
       ]
     }
   }

   // 设备B
   {
     "module": {
       // ...
       "abilities": [
         {
           // ...
           "name": "Ability-deviceB",
           "continueType": ['continueType1'], // 与设备A相同的continueType标签
         }
       ]
     }
   }

注意:

  • continueType在本应用中要保证唯一,字符串以字母、数字和下划线组成,最大长度127个字节,不支持中文。

  • continueType标签类型为字符串数组,如果配置了多个字段,当前仅第一个字段会生效。

6、快速启动目标应用

默认情况下,发起迁移后不会立即拉起对端的目标应用,而是等待迁移数据从源端同步到对端后,才会拉起。

为了发起迁移后能够立即拉起目标应用,做到及时响应,我们可以选择在数据完全同步到对端后,先启动应用。

实现方式是通过在continueType标签中添加“_ContinueQuickStart”后缀进行生效,这样待迁移数据从源端同步到对端后只恢复迁移数据即可(页面提前响应,做好初始化),提升应用迁移体验。示例如下:

{
  "module": {
    // ...
    "abilities": [
      {
        // ...
        "name": "EntryAbility"
        // 如果已经配置了continueType标签,可以在该标签值后添加'_ContinueQuickStart'后缀;
        // 如果没有配置continueType标签,可以使用AbilityName + '_ContinueQuickStart'作为continueType标签实现快速拉起目标应用
        "continueType": ['EntryAbility_ContinueQuickStart'], 
      }
    ]
  }
}