在鸿蒙(HarmonyOS)生态中,分布式任务调度的核心思想是“把合适的任务,交给合适的设备去做”。开发者无需关心底层的网络连接、设备发现或断线重连,只需通过构造包含远端设备标识的 Want 对象,即可将任务分发到另一台设备上执行。

以下是实现跨设备远程启动 Ability 的完整代码:

一、 前置准备:权限配置

要实现跨设备调度,必须在 module.json5 中声明分布式相关的权限:

{
  "module": {
    "requestPermissions": [
      { "name": "ohos.permission.DISTRIBUTED_DATASYNC" },
      { "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" }
    ]
  }
}

二、 核心实战:构造 Want 并远程启动

远程启动 Ability 的核心动作是:构造一个包含远端 deviceId 的 Want,然后调用 startAbility。分布式调度服务(DMS)会在底层自动完成跨端拉起。

1. 源端:发起远程任务调度

假设我们在手机上点击按钮,将任务派发给附近的平板设备执行:

import { distributedDeviceManager } from '@kit.DistributedServiceKit';
import { common } from '@kit.AbilityKit';

export async function dispatchTaskToRemote(context: common.UIAbilityContext) {
    try {
        // 1. 获取可信设备列表,选择目标设备(此处以平板为例)
        const dm = distributedDeviceManager.createDeviceManager('com.example.myapp');
        const deviceList = dm.getAvailableDeviceListSync();
        const targetDevice = deviceList.find(device => device.deviceType === 2); // 假设 2 代表平板

        if (!targetDevice) {
            console.error('未找到合适的远端设备');
            return;
        }

        // 2. 构造包含远端 deviceId 的 Want
        const want = {
            deviceId: targetDevice.networkId, // 指定任务在哪个设备执行
            bundleName: 'com.example.myapp',  // 目标应用的包名
            abilityName: 'RemoteComputeAbility', // 目标 Ability 名称
            parameters: {
                taskId: 'task_1001',
                taskData: '需要远端处理的大数据量'
            }
        };

        // 3. 启动远程 Ability
        await context.startAbility(want);
        console.info(`任务已成功分发至设备: ${targetDevice.deviceName}`);

    } catch (err) {
        console.error('远程任务调度失败:', err);
    }
}
2. 目标端:接收任务并执行

远端设备(如平板)上的 RemoteComputeAbility 被拉起后,直接从 want 中提取参数并执行耗时的业务逻辑:

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

export default class RemoteComputeAbility extends UIAbility {
    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
        // 1. 提取任务参数
        const taskId = want.parameters?.['taskId'] as string;
        const taskData = want.parameters?.['taskData'] as string;

        console.info(`接收到远程任务: ${taskId}`);
        
        // 2. 在远端设备执行真正的业务逻辑(如图片渲染、数据计算)
        this.executeHeavyTask(taskId, taskData);
    }

    private executeHeavyTask(taskId: string, data: string) {
        // 执行耗时任务...
        console.info(`任务 ${taskId} 正在远端执行中...`);
    }
}

三、 进阶架构:任务执行与状态同步解耦

在真实的工程化项目中,强烈不建议通过 Ability 的回调来不断通知源端任务的执行进度(因为 Ability 的生命周期不可控,且可能被系统回收)。

正确的做法是将“任务下发”与“状态同步”解耦:

  1. 任务下发:通过上述的 Want + startAbility 告诉远端“开始干活”。
  2. 状态同步:使用分布式键值数据库(KV-Store)。远端设备在执行任务时,将进度(如 30%running)写入 KV-Store;源设备监听该 Key 的变化,实时在 UI 上更新进度条。
// 远端设备:实时写入任务状态到分布式 KV-Store
import { distributedKVStore } from '@kit.ArkData';

async function updateTaskStatus(taskId: string, status: string) {
    // 假设 kvStore 已经创建并建立跨设备同步
    await kvStore.put(taskId, status); 
    // 源端设备会自动收到 on('dataChange') 事件,从而刷新 UI
}

四、 构建标准化的任务模型(Task Model)

在复杂的分布式系统中,任务必须是“可传输、可落地、可重试”的。建议在发起调度前,定义一个标准化的任务接口,确保跨设备传输的数据具备高度的结构化与可观测性。

// 定义标准化的任务模型
export type TaskType = 'IMAGE_PROCESS' | 'DATA_ANALYSIS' | 'MODEL_INFERENCE';

export interface Task {
    id: string;               // 全局唯一ID(如 UUID)
    type: TaskType;           // 任务类型
    payload: Record<string, Object>; // 任务输入(必须可 JSON 序列化)
    priority?: number;        // 任务优先级(0-100)
    retry?: { times: number; backoffMs: number }; // 重试策略
}

五、 建立双向通信通道(会话管理)

startAbility 仅能实现单向的任务下发。如果源端需要与远端进行高频的“请求-响应”交互,或者需要实时获取计算结果,应使用跨设备 UIAbility 会话管理接口(AbilityConnectionManager)

import { abilityConnectionManager } from '@kit.DistributedServiceKit';

async function establishRemoteSession(targetDevice: distributedDeviceManager.DeviceInfo) {
    // 1. 创建跨设备协同会话
    const sessionId = await abilityConnectionManager.createAbilityConnectionSession({ 
        serviceName: 'dms-compute' 
    });

    // 2. 连接到远端 Ability
    await abilityConnectionManager.connectAbility(sessionId, {
        deviceId: targetDevice.networkId,
        bundleName: 'com.example.myapp',
        abilityName: 'RemoteComputeAbility'
    });

    // 3. 发送计算请求
    await abilityConnectionManager.send(sessionId, { command: 'startCompute', data: [1, 2, 3] });

    // 4. 接收远端响应(支持超时控制)
    const response = await abilityConnectionManager.receive(sessionId, { timeoutMs: 8000 });
    console.info('收到远端计算结果:', response);

    // 5. 释放会话资源
    await abilityConnectionManager.disconnectAbility(sessionId);
    await abilityConnectionManager.closeAbilityConnectionSession(sessionId);
}

六、 任务状态监控与追踪

在任务下发后,源端通常需要知道任务是否成功执行。可以通过分布式任务管理器(distributedMissionManager)获取远端任务的执行状态。

import { distributedMissionManager } from '@kit.DistributedScheduleKit';

async function monitorRemoteTask(missionId: string) {
    try {
        const missionInfo = await distributedMissionManager.getMissionInfo(missionId);
        
        // 获取任务当前状态(running, completed, failed 等)
        console.info(`任务状态: ${missionInfo.state}`);
        // 获取任务进度或返回结果
        console.info(`当前进度: ${missionInfo.parameters?.progress || 0}`);
        
        return missionInfo;
    } catch (error) {
        console.error('获取任务状态失败:', error);
    }
}

七、 智能设备路由(Device Scoring)

鸿蒙分布式调度的核心哲学是“声明需求,而不是指定设备”。在实际工程中,建议实现一套设备评分机制,根据任务的算力、存储、显示需求,动态选择最合适的边缘节点。

// 根据任务需求计算设备得分,选择最优节点
private selectBestDeviceForTask(task: Task, devices: distributedDeviceManager.DeviceInfo[]) {
    let bestDevice: distributedDeviceManager.DeviceInfo | null = null;
    let bestScore = -1;

    for (const device of devices) {
        let score = 0;
        // 1. 信任设备加分
        if (device.isTrusted) score += 10;
        // 2. 根据任务类型进行能力匹配打分
        if (task.type === 'MODEL_INFERENCE' && device.deviceType === 2) { // 平板算力更强
            score += 50;
        }
        if (task.type === 'IMAGE_PROCESS' && device.deviceType === 3) { // 智慧屏适合展示
            score += 30;
        }
        
        if (score > bestScore) {
            bestScore = score;
            bestDevice = device;
        }
    }
    return bestDevice;
}

建议

  1. 计算与展示分离:对于“重计算、轻交互”的任务,目标端强烈建议使用 ServiceExtensionAbility 代替 UIAbility。Service 没有 UI 渲染开销,更适合做后台计算节点。
  2. 避免硬编码设备 ID:永远不要在代码中写死目标设备的 deviceId。应通过 getAvailableDeviceListSync() 动态获取,并结合设备类型(deviceType)或自定义标签进行智能路由。
  3. 完善失败回退机制:分布式环境具有不确定性。若目标设备离线或执行超时(如抛出 116 设备不在线错误),源端必须具备降级策略,将任务回退到本地执行,或提示用户切换设备。
Logo

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

更多推荐