跨设备接续功能上线后,最容易收到的客诉是:“点击流转后,平板上的应用确实打开了,但页面是空的,或者停留在首页,并不是我刚才编辑的界面。”开发者在排查时发现,接续的接口确实被调用了,也没有报错。这种“页面拉起但状态丢失”的断层,通常是因为漏掉了接续过程中的关键生命周期或数据传递规范。

接续不是简单的“应用启动”,它是一套严格的上下文恢复机制。

恢复窗口的同步执行约束

在 onCreate 中处理接续时,很多开发者为了等异步的数据准备,把 restoreWindowStage 放在了 Promise.then 或 setTimeout 里。这会导致接续的恢复时机过期,页面可能拉起,但 UI 树无法正确挂载。

typescript

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
      // 数据写入必须在恢复窗口之前完成
      if (want.parameters) {
        AppStorage.setOrCreate('draftText', want.parameters['draftText'])
      }
      // 必须在同步调用栈中执行
      this.context.restoreWindowStage(new LocalStorage()) 
    }
  }
}

restoreWindowStage 的调用必须处于同步执行流中,一旦外层包裹了异步回调,框架就不会自动恢复之前的页面栈。

冷热启动的双入口处理

单实例模式下,如果目标设备上应用的进程依然存活,接续拉起走的是 onNewWant,而不是 onCreate。如果只在 onCreate 里写了恢复逻辑,遇到热启动场景就会直接跳过,页面自然拿不到流转过来的数据。

标准的做法是抽离一个统一的处理函数,让两个入口都调用它:

typescript

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  this.handleContinue(want, launchParam)
}

onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  this.handleContinue(want, launchParam)
}

private handleContinue(want: Want, launchParam: AbilityConstant.LaunchParam) {
  if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
    // 执行状态恢复逻辑
  }
}

wantParam 容量限制与分流

通过 wantParam 传递数据时,有一个系统硬限制:大小不能超过 100KB。如果用户编辑的是一篇长文,或者带有 base64 图片的表单,序列化后极其容易超限。超限的后果不是报错,而是直接丢弃该字段。

排查时建议在源端的 onContinue 打印数据长度:

typescript

onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
  const draft = AppStorage.get<string>('draftText') || ''
  console.info(`接续数据大小: ${draft.length}`)
  wantParam['draftText'] = draft
  return AbilityConstant.OnContinueResult.AGREE
}

如果数据过大,应当改用分布式数据对象、分布式 KV 或者共享沙箱文件来传递。wantParam 只保留一个唯一标识符或者任务 ID。

Navigation 栈的上下文重建

系统只会自动恢复 Router 的路由栈,对于 Navigation,系统不负责重建页面层级。如果用户在子页面进行流转,目标端拉起的往往只是 Navigation 的根页面。

源端需要在流转时把当前的路由路径记录下来,放入 wantParam。目标端接续成功后,通过 NavPathStack 的手动操作,把对应的子页面 Push 进去,这样才能给用户一个完整的连贯体验。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐