在鸿蒙(HarmonyOS)应用开发中,跨设备迁移(应用接续)是分布式能力的典型体现,旨在让用户在一个设备上操作应用时,能在另一台设备上无缝衔接上一个设备的体验。依托 Ability Kit 与分布式任务调度框架,开发者可实现应用状态与上下文的跨端流转。

一、 核心架构与基本概念

鸿蒙的应用接续底层由分布式调度系统(DSS)与分布式软总线支撑:

  1. Want 协议包:作为设备间传递信息的载体,它是连接源端与宿端的数据桥梁,负责承载 UI 状态、路由信息及业务数据。
  2. 生命周期协调:迁移过程深度绑定 UIAbility 的生命周期。源端经历 onInactive -> onBackground -> onSaveState;宿端经历 onCreate -> onRestoreState -> onWindowStageCreate -> onForeground -> onActive
  3. 数据量级约束:为保证传输性能,通过 wantParam 传递的接续数据需控制在 100KB 以下;若涉及大数据量,需使用分布式对象或分布式文件系统迁移。

二、 核心开发能力与流转机制

  1. 设备发现与权限配置:应用需声明 ohos.permission.DISTRIBUTED_DATASYNC 权限(API12起无需申请),并确保双端设备登录同一华为账号、开启蓝牙/WLAN及“接续”开关。
  2. 源端状态封包:在源端 Ability 的 onContinue 钩子中,收集当前 UI 状态及临时数据,打包存入 wantParams,并返回 AGREE 允许流转。
  3. 宿端现场恢复:目标端在冷启动或热拉起时,解析 want.parameters 中的续接数据,重塑界面状态(如恢复输入框草稿、光标偏移量等)。
  4. 源端终结与退出:接续传输完毕后,源端应用根据系统决策调用 onDestroy() 释放本地资源,或根据业务需求保留在后台(如直播、听书场景)。

三、 性能优化

在实际落地跨设备迁移时,需特别注意以下规范:

  1. 敏感数据加密:跨设备传输的敏感内容应通过 @ohos.security.huks 加密后再传输,保障用户隐私安全。
  2. 失败回退机制:若目标设备离线或传输中断,应用应提示用户并保留本地任务状态,避免数据丢失。
  3. 带宽优化:仅传递必要的状态数据,避免大文件直接通过 wantParams 迁移,防止传输超时和性能骤降。
  4. 按需退出策略:默认情况下接续完成后源端会自动退出,但针对会议、直播等场景,开发者需配置源端仅暂停或保持运行,以保障业务连续性。

四、 应用实战:源端数据封包与宿端状态恢复

在鸿蒙 ArkTS 开发中,跨设备迁移的核心在于源端精准打包状态,以及宿端无缝解析并重塑 UI。

  1. 源端状态封包(onContinue)
    在源端 UIAbility 的 onContinue 生命周期中,收集当前页面的关键业务状态(如文档 ID、输入内容、光标位置等),将其写入 wantParam 对象,并返回 continuationManager.ContinuationState.AGREE 以允许系统执行迁移。
  2. 宿端现场恢复(onCreate & UI 渲染)
    目标设备在拉起应用时,会在 onCreate 的 want 参数中接收到 continuation 标识及对应的 parameters。开发者需解析这些数据,存入全局状态或 AppStorage,并在页面组件的 aboutToAppear 中读取并恢复 UI 状态。
  3. 确认迁移完成
    宿端成功恢复状态后,应调用 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);
    }
}

五、 进阶场景:大数据量迁移与按需退出策略

针对复杂的业务场景,开发者需灵活处理大文件传输与源端生命周期。

  1. 大数据量分布式迁移
    当接续同步的内容(如高清图片、大段富文本)超过 100KB 限制时,严禁直接塞入 wantParam。应将大文件存入分布式文件系统(distributedFilesDir)或分布式数据对象,仅在 wantParam 中传递文件的相对路径或 Key,由宿端通过分布式能力拉取实体数据。
  2. 差异化退出策略配置
    系统默认在接续完成后自动销毁源端应用。但在特定场景下需特殊处理:例如视频/音频播放、直播类应用,源端应配置为“不退出且继续播放”或“不退出仅暂停”;会议类应用可配置为“退出会议但保留聊天界面”。开发者需在 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: 确保本地任务状态未被意外清除,允许用户继续在当前设备操作
        }
    }
}

 

 

Logo

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

更多推荐