分布式任务调度:远程启动Ability实战(65)
在鸿蒙(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 的生命周期不可控,且可能被系统回收)。
正确的做法是将“任务下发”与“状态同步”解耦:
- 任务下发:通过上述的
Want + startAbility告诉远端“开始干活”。 - 状态同步:使用分布式键值数据库(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;
}
建议
- 计算与展示分离:对于“重计算、轻交互”的任务,目标端强烈建议使用
ServiceExtensionAbility代替UIAbility。Service 没有 UI 渲染开销,更适合做后台计算节点。 - 避免硬编码设备 ID:永远不要在代码中写死目标设备的
deviceId。应通过getAvailableDeviceListSync()动态获取,并结合设备类型(deviceType)或自定义标签进行智能路由。 - 完善失败回退机制:分布式环境具有不确定性。若目标设备离线或执行超时(如抛出
116设备不在线错误),源端必须具备降级策略,将任务回退到本地执行,或提示用户切换设备。
更多推荐



所有评论(0)