手机看一半,换平板还得重新找进度?

不知道大家有没有这种体验:在手机上看着一篇长文,或者正在编辑一个备忘录,突然想换平板接着看。结果打开平板,发现应用虽然能登录,但刚才看的内容和进度全没了。

这体验简直割裂。

HarmonyOS 最核心的卖点之一就是“分布式”,其中“应用接续”(Migration/Continuation)就是解决这个痛点的终极方案。用户只需要在手机底部轻轻上滑,选择平板图标,应用就“飞”到了平板上,不仅状态还在,进度甚至能精确到秒。

听起来很科幻对吧?很多同学去官网翻了半天 API,代码写完了,结果两台设备死活连不上,或者流转过去直接闪退。

这篇就把应用接续的完整链路拆开,带你避过所有坑。

https://placeholder-01.jpg/

一、接续不是魔法,先搞懂它的约束条件

写代码之前,必须先明确几件事,不然你 debug 到凌晨都找不到原因。

  1. 必须登录同一个华为账号(这是基础中的基础,账号不同,软总线直接屏蔽)。

  2. 两台设备必须开启蓝牙和 WLAN(分布式软总线底层依赖这些通信能力)。

  3. 设备必须在同一个局域网,或者靠近。

  4. bundleName 必须完全一致(应用包名不同,系统怎么知道要把状态给谁?)。

而且,应用接续不是自动的。系统只提供“流转的通道”,具体存什么状态、怎么恢复,全都要你自己写。

二、核心 API:onContinue 与 onCreate/onNewWant

应用接续的核心生命周期是 UIAbility 的 onContinue 和 onCreate/onNewWant。

当用户在手机上点击“接续”按钮,系统会调用源设备 Ability 的 onContinue:

typescript

import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';

export default class EntryAbility extends UIAbility {
  onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
    // 保存当前状态
    const currentProgress = AppStorage.get<number>('readProgress') || 0;
    const articleId = AppStorage.get<string>('currentArticleId') || '';

    // 把要流转的数据塞进 wantParam
    wantParam['progress'] = currentProgress;
    wantParam['articleId'] = articleId;
    wantParam['timestamp'] = Date.now();

    console.info('onContinue 保存状态成功');
    return AbilityConstant.OnContinueResult.AGREE; // 返回同意接续
  }
}

坑来了:onContinue 必须是同步的,且必须在 1 秒内返回!

你千万不要在这里面做网络请求、读大文件或者耗时计算。它只能用来同步地把内存里的状态塞进 wantParam。如果你写得慢,系统会认为你放弃了接续,直接返回 REJECT。

那如果数据很大怎么办?比如用户编辑了一个几十兆的文档。

答案是:只传关键标识(如文档 ID 和版本号),具体数据让目标设备自己去云端或者本地分布式文件系统拉取。

三、目标设备恢复:onCreate 与 onNewWant 的双重考验

数据传过来了,目标设备(平板)怎么接?

这取决于平板上应用的状态:

  • 如果平板上应用没启动,走 onCreate。

  • 如果平板上应用已经在后台,走 onNewWant。

所以,恢复逻辑必须写两遍(或者抽成一个公共函数):

typescript

export default class EntryAbility extends UIAbility {
  onCreate(want, launchParam) {
    if (want.parameters && want.parameters.articleId) {
      this.restoreState(want.parameters);
    }
  }

  onNewWant(want, launchParam) {
    if (want.parameters && want.parameters.articleId) {
      this.restoreState(want.parameters);
    }
  }

  private restoreState(params: Record<string, Object>) {
    const articleId = params.articleId as string;
    const progress = params.progress as number;
    
    // 把数据存到 AppStorage,页面加载时读取
    AppStorage.setOrCreate('restoredArticleId', articleId);
    AppStorage.setOrCreate('restoredProgress', progress);
    
    // 如果需要跳转到指定页面
    // 注意:不要在这里直接 router.pushUrl,可能页面栈还没准备好
  }
}

这里有个让人痛不欲生的坑:冷启动时的页面跳转。

平板上的应用是冷启动,onCreate 执行时,UI 的首页可能还没加载完(甚至还没创建 WindowStage)。你在 onCreate 里调 router.pushUrl,100% 失败或者白屏。

正确的解法是: 用 AppStorage 暂存数据。首页的 aboutToAppear 或 onPageShow 里读取这个数据,再决定是否跳转。

typescript

// Index.ets
aboutToAppear() {
  const restoredArticleId = AppStorage.get<string>('restoredArticleId');
  if (restoredArticleId) {
    // 数据到了,再跳转
    router.pushUrl({ url: 'pages/ArticlePage', params: { articleId: restoredArticleId } });
    // 清除标记,防止下次热启动重复跳转
    AppStorage.setOrCreate('restoredArticleId', undefined);
  }
}

https://placeholder-02.png/

四、动态选择设备:别让用户手动挑

很多时候,用户不止一台设备。手机旁边可能有个平板,还有个智慧屏。系统怎么知道流转到哪台?

系统会弹出一个设备选择列表。但如果你想让体验更好,可以用 distributedDeviceManager 主动获取可流转的设备:

typescript

import deviceManager from '@ohos.distributedDeviceManager';

const dm = deviceManager.createDeviceManager('com.example.myapp');
const deviceList = dm.getAvailableDeviceListSync();

// 你可以根据设备类型、距离等条件,自动选中一个设备进行流转

不过,生产环境里建议还是给用户选择权,别自作聪明。用户可以自己决定是流转到平板,还是智慧屏。

另外,流转成功或失败,都要给用户明确的提示,别静静地什么都不做。

五、踩坑合集:那些让你怀疑人生的瞬间

坑一:接续失败了,但没有任何报错。

先检查账号是否一致,再检查网络(必须同一局域网或者蓝牙配对)。最后检查 bundleName 是否一样。这三个错一点,系统直接静默失败,连日志都不给你留。

坑二:传中文乱码。

wantParam 里传字符串没问题,但如果传复杂对象,一定要序列化。推荐用 JSON.stringify 转成字符串再传,接收方再 JSON.parse。别传 ArrayBuffer,那个坑更多。

坑三:恢复状态后,页面闪烁。

如果你在 Index 页面 aboutToAppear 里先渲染了默认的空白状态,等 AppStorage 数据到了再跳转,中间会有个短暂的闪烁。解法是:加一个 @State isLoading = true 的状态,数据没准备好之前,先显示一个 Loading 或者骨架屏。

https://placeholder-03.png/

六、总结

应用接续是 HarmonyOS 分布式能力最具感知度的功能。核心记住三点:

  1. onContinue 必须同步返回,只传关键状态(ID、进度),不要传大数据。

  2. 恢复状态时,冷启动要等页面加载完再跳转,用 AppStorage 做数据中转。

  3. 账号、网络、bundleName 是隐形门槛,排查问题时首先查这三样。

Logo

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

更多推荐