本节摘要:流转(continuation)让 UIAbility 的界面与状态跨设备迁移。本节配置流转能力、实现"从手机迁到平板"的完整链路,讲清随行状态的组织方式与分布式数据对象的最小用法,并给出失败兜底的设计。
场景直白:用户在手机上写备忘写到一半,想换平板的大屏继续。期望的体验是点一下"流转",平板亮起,内容原样出现,手机侧收尾。拆开看这条链路要做四件事:配置声明应用支持迁移、发起方选择目标设备并请求迁移、对端被拉起并恢复状态、发起方善后。
第一步是配置。在模块的 module.json5 里给 EntryAbility 开启迁移能力:
{ "module": { "abilities": [ { "name": "EntryAbility", "srcEntry": "./ets/entryability/EntryAbility.ets", "continuable": true } ] } }
continuable 一行就是应用的迁移开关。第二步,界面上的"流转"按钮调用迁移接口:
import { continuationManager } from '@kit.DistributedServiceKit'; import { BusinessError } from '@kit.BasicServicesKit'; async function startContinuation(): Promise<void> { try { const token = await continuationManager.startContinuationDeviceManager( undefined, continuationManager.ContinuationMode.PICK ); hilog.info(0x0001, 'Continuation', '选择器令牌已获取'); } catch (e) { const err = e as BusinessError; hilog.error(0x0001, 'Continuation', `发起失败 ${err.code}`); } }
PICK 模式会拉起系统的设备选择器(也可以自绘设备列表后用指定模式直连)。用户在弹出的面板里选中平板,系统接管后续:对端拉起同应用、发起方收到回调。第三、四步都发生在 UIAbility 的生命周期回调里——这正是第 2 章埋的伏笔兑现的地方:迁移前后,onContinue 准备随行数据:
import { AbilityConstant } from '@kit.AbilityKit'; import Want from '@ohos.app.ability.Want'; export default class EntryAbility extends UIAbility { onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult { wantParam['draft'] = this.currentDraft; wantParam['scrollOffset'] = this.scrollOffset; return AbilityConstant.OnContinueResult.AGREE; } }
onContinue 在迁移发起时被调用:把要带走的轻量数据塞进 wantParam,返回 AGREE 表示同意迁移。对端拉起时走 onCreate,从 want 里取回:
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) { this.currentDraft = (want.parameters?.['draft'] as string) ?? ''; this.scrollOffset = (want.parameters?.['scrollOffset'] as number) ?? 0; } }
LaunchReason 为 CONTINUATION 标明这是一次迁移启动而非冷启动,随行数据从 want.parameters 取回,页面加载后恢复滚动位置与草稿内容。

onContinue 适合"搬一次",两台设备要持续看同一份数据(比如双端协作编辑),用分布式数据对象。它是把一个 JS 对象挂到分布式会话上,任何一端改,另一端自动收到:
import { distributedDataObject } from '@kit.ArkData'; let session: distributedDataObject.DataObject | undefined; async function joinCollab(context: Context, docId: string): Promise<void> { const target: Record<string, Object> = { 'docId': docId, 'text': '' }; session = distributedDataObject.create(context, target); session.setSessionId(`collab-${docId}`); session.on('change', () => { const text = (session!.getObject()['text'] as string); this.applyRemoteText(text); }); } function editText(next: string): void { session?.put('text', next); }
setSessionId 让两端加入同一会话(会话标识用业务 ID 拼),put 修改字段,change 事件在对端触发。它的心智模型与第 4 章的状态装饰器神似——也是"改数据、界面自动跟",只是作用域从组件树扩大到了设备群。数据量与复杂查询不适用(那是分布式 KV 表的领域,思路相同接口更全),协作场景的小对象正合适。
迁移不是总能成功:对端离线、版本不匹配、用户取消。设计上有三条纪律。其一,onContinue 里不要做耗时操作,准备数据要快,超时会被系统放弃迁移。其二,失败路径给用户明确反馈并保持本端状态无损——迁移失败时用户应该"什么都没发生"般继续编辑,而不是丢了一半状态。其三,两端版本兼容:wantParam 的数据格式随版本演进时,接收端要能容错旧格式,缺字段走默认值(第 6.1 节解析纪律的同款思路)。
演练收尾:在双模拟器上完整走一遍——手机写半句备忘、点流转、选平板、平板出现同内容且光标位置合理;再测失败路径(迁移前关掉平板实例),确认手机侧提示明确、编辑状态无损。两个实验都过关,你才算真正"会用"流转而不只是"会调"接口。
⚠️ 常见坑:把大数据(整份文档、图片转义串)塞进 onContinue 的 wantParam。随行数据有大小约束,超限迁移静默失败。大对象走分布式数据对象或先落库再迁移后拉取,wantParam 只放"最小现场"。
💡 关键直觉:迁移的本质是"重建上下文"而不是"搬运进程"。想清楚用户继续工作需要哪些最小上下文(草稿、位置、选中项),迁移设计就完成了大半——这也是评估任何"接续体验"产品的通用标尺。
本节要点回顾:
界面可以跨设备了,服务还能更轻一步——不安装应用就触达用户。下一节的元服务与卡片,是 HarmonyOS 服务生态的另一种交付形态。