把应用从一个设备搬到另一个设备,无缝衔接,不让用户感觉到"断"


什么是应用接续

用户在手机上编辑备忘录,走到办公室后切换到平板上继续编辑。这不是简单的"重新打开",而是把手机上的编辑进度、光标位置、甚至未保存的内容,全部搬到平板上。

这就是应用接续——当用户在一个设备上操作应用时,可以在另一个设备的相同应用中快速切换,无缝衔接上一个设备的应用体验。

鸿蒙系统已经把底层的事情干完了:设备发现、连接、组网,都不需要你操心。你只需要关注两件事:怎么保存数据,怎么恢复数据。


实现原理

image.png

接续的前提条件

不是随便就能接续的,有硬性条件:

设备条件

  • HarmonyOS NEXT Developer Preview0 及以上版本
  • 双端设备登录同一华为账号
  • 双端设备打开 WLAN 和蓝牙开关,或者启用"多设备协同增强服务"
  • 双端设备在"设置"中开启"多设备协同 > 接续"功能
  • 双端设备都安装了该应用

数据条件

  • onContinue 回调中传输的数据控制在 100KB 以下
  • 大数据量请用分布式对象迁移

权限条件(API11及以前版本):

  • 需要声明 ohos.permission.DISTRIBUTED_DATASYNC 权限
  • 需要向用户申请授权

API12起,不再需要申请权限。


哪些场景需要接续?哪些不需要?

不是所有应用都适合接续,要看场景:

工具类(备忘录、笔记、日历、邮箱、浏览器)

场景:备忘录详情页、笔记编辑页、日历日程页面、邮件编辑页面、网页详情页

同步内容:浏览进度、编辑进度、附件

源端处理:退出

比如你在手机上写邮件,写到一半换到平板继续写。手机上的邮件应用可以退出,因为任务已经搬到平板上了。

出行导航类(地图服务)

场景:路线查询、导航界面

同步内容:当前路线、导航进度

源端处理:退出

影音娱乐类(视频、音乐、直播)

场景:音视频播放页、直播页

同步内容:播放进度、直播进度

源端处理:按需适配

这类应用比较特殊。音乐播放时,源端可以选择不退出,只暂停播放;直播时,源端可以不退出也不暂停,因为用户可能想两边都看。

新闻阅读类(听书、电子书、新闻)

场景:书籍首页、小说阅读页、听书页、新闻详情页

同步内容:阅读进度、听书进度

源端处理:退出,但听书或直播类可以不退出

办公类(会议、CAD编辑)

场景:会议界面、CAD编辑界面

同步内容:会议状态、编辑内容

源端处理

  • 会议:不退出应用,只退出会议,返回聊天框界面
  • CAD:按需适配

社交通讯类(社交媒体平台)

场景:图文浏览、视频浏览、编辑页

同步内容:浏览进度、编辑进度

源端处理:按需适配

其他类(拍摄美化、便捷生活、金融理财、游戏)

按需适配。核心原则:看用户是否需要同时在多端使用,如果需要,源端不退出;如果不需要,源端退出。


接续怎么运作?

三个步骤:源端保存 → 系统传输 → 对端恢复
image.png

源端:onContinue() 回调

用户触发迁移时,源端的 onContinue() 被调用。你要在这里保存迁移数据。

onContinue(wantParam: Record<string, Object>) {
  // 获取对端应用版本号,做兼容校验
  const targetVersion = wantParam.version;
  if (targetVersion < 0) {
    promptAction.showToast({
      message: '对端应用版本号过低,不支持接续',
      duration: 2000
    });
    return AbilityConstant.OnContinueResult.MISMATCH;
  }

  // 保存迁移数据
  const continueInput = '迁移的数据';
  if (continueInput) {
    wantParam['data'] = continueInput;
  }

  return AbilityConstant.OnContinueResult.AGREE;
}

返回值决定是否支持迁移:

  • AGREE:同意
  • REJECT:拒绝(应用异常时)
  • MISMATCH:版本不匹配

wantParam 中的系统预置字段:

  • version:对端应用版本号
  • targetDevice:对端设备的 networkId

对端:onCreate() / onNewWant() 回调

对端启动时,根据启动模式调用不同接口:

  • 冷启动或多实例应用热启动:onCreate()
  • 单实例应用热启动:onNewWant()
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
    // 获取迁移数据
    let continueInput = '';
    if (want.parameters !== undefined) {
      continueInput = JSON.stringify(want.parameters.data);
    }

    // 恢复页面栈
    this.context.restoreWindowStage(this.storage);
  }
}

重要:restoreWindowStage() 必须在同步接口方法中执行,如果在异步回调中执行,页面可能加载失败。


开发步骤

1. 启用接续能力

在 module.json5 中配置:

{
  "module": {
    "abilities": [
      {
        "continuable": true
      }
    ]
  }
}

2. 配置启动模式

参考 UIAbility 组件启动模式,根据业务需求选择。

3. 源端保存数据

在 onContinue() 中保存数据,可选做兼容校验。

4. 对端恢复数据

在 onCreate() / onNewWant() 中恢复数据,调用 restoreWindowStage() 恢复页面栈。
image.png

高级配置

同应用不同 Ability 迁移

如果应用在不同设备上的 Ability 名称不同(比如手机叫 EntryAbility,平板叫 PadAbility),需要用 continueType 关联:

// 设备A
{
  "abilities": [
    {
      "name": "Ability-deviceA",
      "continueType": ['continueType1']
    }
  ]
}

// 设备B
{
  "abilities": [
    {
      "name": "Ability-deviceB",
      "continueType": ['continueType1']
    }
  ]
}

continueType 必须一致,在本应用中唯一,最大长度127字节,不支持中文。

同应用不同 BundleName 迁移

如果应用在不同设备上使用不同的 BundleName(比如手机是 com.demo.example1,平板是 com.demo.example2),需要配置 continueBundleName:

// 设备A(BundleName: com.demo.example1)
{
  "abilities": [
    {
      "continueType": ['continueType'],
      "continueBundleName": ["com.demo.example2"]
    }
  ]
}

// 设备B(BundleName: com.demo.example2)
{
  "abilities": [
    {
      "continueType": ['continueType'],
      "continueBundleName": ["com.demo.example1"]
    }
  ]
}

如果是不同开发者发布的应用,需要在 AppGallery Connect 平台申请"接续服务",填写对方的 APP ID。

快速启动目标应用

默认情况下,发起迁移后不会立即拉起对端应用,而是等待数据传输完成。如果想立即拉起,减少等待时间,可以在 continueType 后添加 “_ContinueQuickStart” 后缀:

{
  "abilities": [
    {
      "continueType": ['EntryAbility_ContinueQuickStart']
    }
  ]
}

配置快速启动后,会收到两次启动请求:

  • 第一次:PREPARE_CONTINUATION(提前拉起)
  • 第二次:CONTINUATION(接续拉起)

动态配置迁移能力

默认状态下,迁移能力是开启的(ACTIVE)。如果需要只在某些页面或某些事件发生时才支持迁移,可以动态设置:

// onCreate() 中关闭迁移能力
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE, (result) => {
    console.info(`setMissionContinueState: ${JSON.stringify(result)}`);
  });
}

// 特定页面的 onPageShow() 中打开迁移能力
onPageShow() {
  this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
    console.info('setMissionContinueState ACTIVE result: ', JSON.stringify(result));
  });
}

重要:迁移完成后,要重新设置为 ACTIVE,保证迁移的连续性。

按需迁移页面栈

默认情况下,系统会自动恢复页面栈。如果不想用默认页面栈,可以设置不迁移:

onContinue(wantParam: Record<string, Object>) {
  wantParam[wantConstant.Params.SUPPORT_CONTINUE_PAGE_STACK_KEY] = false;
  return AbilityConstant.OnContinueResult.AGREE;
}

onWindowStageRestore(windowStage: window.WindowStage) {
  windowStage.loadContent('continue/PageName', (err, data) => {
    // ...
  });
}

当前仅支持 router 路由的页面栈自动恢复,navigation 路由需要手动处理。

按需退出

默认情况下,迁移成功后源端应用会退出。如果不想退出,可以设置:

onContinue(wantParam: Record<string, Object>) {
  wantParam[wantConstant.Params.SUPPORT_CONTINUE_SOURCE_EXIT_KEY] = false;
  return AbilityConstant.OnContinueResult.AGREE;
}

常见问题排查

对端不显示接续图标

检查四个条件:

  1. 蓝牙是否已开启
  2. 接续功能是否已开启(设置 -> 多设备协同 -> 接续)
  3. 是否已登录相同的华为账号
  4. 组网是否成功(命令行检查:hidumper -s 4700 -a "buscenter -l remote_device_info"

1分钟后图标消失

这是系统的特性,不是 bug。图标在最后一次触屏操作后的1分钟内保持显示,超过1分钟会自动隐藏,减少对用户的干扰。再次操作应用时,图标会重新出现。

新接入的应用不出现图标

检查三个条件:

  1. 两端都已安装应用
  2. continuable 标签设置为 true
  3. 应用没有调用 setMissionContinueState() 将迁移状态设置为 false

避坑指南

restoreWindowStage() 不要放在异步回调里:会导致页面加载失败。

onWindowStageCreate() 的初始化要在 onWindowStageRestore() 里重做:迁移后不执行 onWindowStageCreate(),只执行 onWindowStageRestore()。

数据不要超过 100KB:大数据用分布式对象迁移。

continueType 不要用中文:最大长度127字节,只支持字母、数字、下划线。

迁移后要重新设置迁移状态为 ACTIVE:保证迁移的连续性。


总结

应用接续的核心,就是"搬"——把用户的应用状态从一个设备搬到另一个设备。鸿蒙系统已经把底层的事干完了,你只需要在源端保存数据,在对端恢复数据。

关键步骤:

  1. 配置 continuable 标签启用接续能力
  2. 在 onContinue() 中保存数据(可选做版本兼容校验)
  3. 在 onCreate() / onNewWant() 中恢复数据,调用 restoreWindowStage()

高级能力:

  • continueType 关联不同 Ability
  • continueBundleName 关联不同 BundleName
  • 快速启动减少等待时间
  • 动态配置迁移能力
  • 按需迁移页面栈和按需退出

记住:接续不是重新打开,而是无缝衔接。用户不应该感觉到"断",任务应该像流水一样自然流动。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐