跨设备迁移:应用状态无缝流转到其他设备(210)
·
在鸿蒙(HarmonyOS)应用开发中,跨设备迁移(应用接续)是分布式能力的典型体现,旨在让用户在一个设备上操作应用时,能在另一台设备上无缝衔接上一个设备的体验。依托 Ability Kit 与分布式任务调度框架,开发者可实现应用状态与上下文的跨端流转。
一、 核心架构与基本概念
鸿蒙的应用接续底层由分布式调度系统(DSS)与分布式软总线支撑:
- Want 协议包:作为设备间传递信息的载体,它是连接源端与宿端的数据桥梁,负责承载 UI 状态、路由信息及业务数据。
- 生命周期协调:迁移过程深度绑定 UIAbility 的生命周期。源端经历
onInactive->onBackground->onSaveState;宿端经历onCreate->onRestoreState->onWindowStageCreate->onForeground->onActive。 - 数据量级约束:为保证传输性能,通过
wantParam传递的接续数据需控制在 100KB 以下;若涉及大数据量,需使用分布式对象或分布式文件系统迁移。
二、 核心开发能力与流转机制
- 设备发现与权限配置:应用需声明
ohos.permission.DISTRIBUTED_DATASYNC权限(API12起无需申请),并确保双端设备登录同一华为账号、开启蓝牙/WLAN及“接续”开关。 - 源端状态封包:在源端 Ability 的
onContinue钩子中,收集当前 UI 状态及临时数据,打包存入wantParams,并返回AGREE允许流转。 - 宿端现场恢复:目标端在冷启动或热拉起时,解析
want.parameters中的续接数据,重塑界面状态(如恢复输入框草稿、光标偏移量等)。 - 源端终结与退出:接续传输完毕后,源端应用根据系统决策调用
onDestroy()释放本地资源,或根据业务需求保留在后台(如直播、听书场景)。
三、 性能优化
在实际落地跨设备迁移时,需特别注意以下规范:
- 敏感数据加密:跨设备传输的敏感内容应通过
@ohos.security.huks加密后再传输,保障用户隐私安全。 - 失败回退机制:若目标设备离线或传输中断,应用应提示用户并保留本地任务状态,避免数据丢失。
- 带宽优化:仅传递必要的状态数据,避免大文件直接通过
wantParams迁移,防止传输超时和性能骤降。 - 按需退出策略:默认情况下接续完成后源端会自动退出,但针对会议、直播等场景,开发者需配置源端仅暂停或保持运行,以保障业务连续性。
四、 应用实战:源端数据封包与宿端状态恢复
在鸿蒙 ArkTS 开发中,跨设备迁移的核心在于源端精准打包状态,以及宿端无缝解析并重塑 UI。
- 源端状态封包(onContinue)
在源端 UIAbility 的onContinue生命周期中,收集当前页面的关键业务状态(如文档 ID、输入内容、光标位置等),将其写入wantParam对象,并返回continuationManager.ContinuationState.AGREE以允许系统执行迁移。 - 宿端现场恢复(onCreate & UI 渲染)
目标设备在拉起应用时,会在onCreate的want参数中接收到continuation标识及对应的parameters。开发者需解析这些数据,存入全局状态或AppStorage,并在页面组件的aboutToAppear中读取并恢复 UI 状态。 - 确认迁移完成
宿端成功恢复状态后,应调用continuationManager.completeContinuation(this.context, true)通知系统迁移成功,系统随后会接管源端应用的退出逻辑。
// SourceAbility.ets
import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';
import continuationManager from '@ohos.continuationManager';
export default class SourceAbility extends UIAbility {
// 1. 在 onCreate 中注册续接模式
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
continuationManager.setContinuationMode(this.context, continuationManager.ContinuationMode.AUTO);
}
// 2. 源端状态封包:系统回调此方法时,收集并传递必要状态
async onContinue(wantParam: Record<string, Object>): Promise<AbilityConstant.OnContinueResult> {
try {
// 核心:仅传递轻量级状态数据(控制在 100KB 以内)
wantParam['docId'] = 'DOC_20260105';
wantParam['content'] = 'Hello from Phone!';
wantParam['cursorPos'] = 18;
// 返回 AGREE 允许系统执行流转
return AbilityConstant.OnContinueResult.AGREE;
} catch (error) {
console.error('状态封包失败:', error);
return AbilityConstant.OnContinueResult.REJECT;
}
}
}
/*
* 附:module.json5 必须开启接续能力
* "abilities": [{
* "name": "SourceAbility",
* "continuable": true
* }]
*/
// TargetAbility.ets
import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';
import continuationManager from '@ohos.continuationManager';
export default class TargetAbility extends UIAbility {
storage: LocalStorage = new LocalStorage();
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 核心:判断是否为接续启动
if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
this.restoreContinuationData(want);
}
}
// 解析续接数据并保存至 LocalStorage/AppStorage
private restoreContinuationData(want: Want): void {
if (want.parameters) {
const docId = want.parameters['docId'] as string;
const content = want.parameters['content'] as string;
const cursorPos = want.parameters['cursorPos'] as number;
this.storage.setOrCreate('resumeData', { docId, content, cursorPos });
console.info('宿端状态恢复成功');
}
// 核心:确认续接成功,系统随后会接管源端退出逻辑
continuationManager.completeContinuation(this.context, true);
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// 传入 LocalStorage,使页面组件能读取到恢复的数据
windowStage.loadContent('pages/Editor', (err) => {
if (err.code) return;
}, this.storage);
}
}
五、 进阶场景:大数据量迁移与按需退出策略
针对复杂的业务场景,开发者需灵活处理大文件传输与源端生命周期。
- 大数据量分布式迁移
当接续同步的内容(如高清图片、大段富文本)超过 100KB 限制时,严禁直接塞入wantParam。应将大文件存入分布式文件系统(distributedFilesDir)或分布式数据对象,仅在wantParam中传递文件的相对路径或 Key,由宿端通过分布式能力拉取实体数据。 - 差异化退出策略配置
系统默认在接续完成后自动销毁源端应用。但在特定场景下需特殊处理:例如视频/音频播放、直播类应用,源端应配置为“不退出且继续播放”或“不退出仅暂停”;会议类应用可配置为“退出会议但保留聊天界面”。开发者需在module.json5中按垂类规范配置continuationPolicy。
// AdvancedContinuation.ets
import { distributedDataObject } from '@kit.ArkData';
import { AbilityConstant } from '@kit.AbilityKit';
export class AdvancedContinuation {
// 1. 大数据量迁移:通过分布式数据对象传递大文本/文件路径
public static async migrateLargeData(context: Context, wantParam: Record<string, Object>, largeContent: string) {
// 核心:严禁将大文件直接塞入 wantParam
const dataObject = distributedDataObject.create(context, { content: largeContent });
const sessionId = distributedDataObject.genSessionId();
dataObject.setSessionId(sessionId);
// 将 sessionId 传给宿端,宿端通过此 ID 拉取实体数据
wantParam['dataSessionId'] = sessionId;
// 持久化数据,确保源端退出后宿端仍可获取
await dataObject.save(wantParam.targetDevice as string);
}
}
/*
* 2. 差异化退出策略配置 (module.json5)
* 针对直播/听书等场景,配置源端接续后不退出或仅暂停
* "abilities": [{
* "name": "LiveAbility",
* "continuable": true,
* "continuationPolicy": "suspend" // 可选: exit(默认), suspend, none
* }]
*/
// MigrationSafetyDemo.ets
import continuationManager from '@ohos.continuationManager';
import distributedSchedule from '@ohos.distributedSchedule';
export class MigrationSafetyDemo {
// 安全发起迁移,包含失败回退机制
public static async safeMigrate(context: Context) {
try {
const deviceList = await distributedSchedule.getTrustedDeviceList();
const targetDevice = deviceList.find(d => d.deviceType === 3); // 查找平板
if (!targetDevice) {
console.warn('未找到可用目标设备');
return;
}
await continuationManager.startContinuation(
context,
targetDevice.deviceId,
'com.example.app.TargetAbility',
{} // wantParams 由 onContinue 回调填充
);
} catch (error) {
// 核心:失败回退机制
console.error('跨设备迁移失败:', error.message);
// TODO: 提示用户网络异常或目标设备离线
// TODO: 确保本地任务状态未被意外清除,允许用户继续在当前设备操作
}
}
}
更多推荐



所有评论(0)