《HarmonyOS 7 精准碰一碰跨设备协作开发实战》05:从文件投递到跨设备业务指令协同【鸿蒙心迹】
前面都是传文件,这次升级一下:手机碰PC左边是加素材,碰中间是插画布,碰右边是换预览。同一个资源,不同位置,不同动作。

前四篇我们解决了"传什么、传到哪、怎么传不乱"。
这一篇解决一个更有意思的问题:传过去以后干什么?
我之前测的时候,手机上一张图,碰PC左边素材栏,图片加进去了;碰中间画布,图片也加进去了;碰右边预览窗口,图片还是加进去了。
不对啊——这三个地方应该有不同动作才对。
碰左边:应该是"添加到素材库"。
碰中间:应该是"插入到当前画布"。
碰右边:应该是"替换预览内容"。
但我之前的代码,不管碰哪里,都是同一个处理:存URI、加列表。
问题出在哪?我只传了文件,没传业务指令。
一、传输层和业务层要分开
很多人做跨设备分享,容易犯一个错:把"传文件"和"怎么处理文件"写在一个回调里。
接收回调一进来,立刻if/else判断是哪个区域,然后直接调对应的业务方法。
短代码看着挺爽,但业务一多就炸了。
比如现在有3个区域对应3个动作。以后加了一个"碰图层面板",要插入到指定图层,你又得在回调里加一个if。再加一个"碰导出按钮",要导出当前资源,再加一个if。
回调变成了if/else大杂烩,谁都不敢动。
正确的做法是分两层:
传输层: 只负责把数据传过来,不关心怎么处理。
业务层: 收到数据后,根据指令分发到不同处理器。
中间用一个统一的Payload把业务信息封装起来。

二、CrossDevicePayload:统一传输协议
我们定义一个统一的数据结构CrossDevicePayload。
它不只装URI,还要装:
- resourceId:资源唯一标识
- action:要执行什么动作
- target:目标区域
- sourceDevice:来源设备
- timestamp:时间戳
- params:业务参数
这样接收端收到后,根据action字段,分发到不同的业务处理器。
这段代码解决什么问题: 统一跨设备传输协议。
文件: protocol/CrossDevicePayload.ets
用途: 封装传输数据和业务指令
接入位置: 发送端组装、接收端解析
enum CrossAction {
ADD_TO_MATERIAL = 'add_material', // 添加到素材库
INSERT_CANVAS = 'insert_canvas', // 插入画布
REPLACE_PREVIEW = 'replace_preview', // 替换预览
EXPORT_RESOURCE = 'export_resource' // 导出资源
}
interface CrossDevicePayload {
resourceId: string;
uri: string;
action: CrossAction;
target: string;
sourceDevice: string;
timestamp: number;
params: Record<string, string>;
}
// 发送端组装
function buildPayload(action: CrossAction, uri: string, target: string): CrossDevicePayload {
return {
resourceId: Date.now().toString(),
uri: uri,
action: action,
target: target,
sourceDevice: 'phone_' + deviceInfo.getDeviceId(),
timestamp: Date.now(),
params: {}
};
}
三、业务分发器:不要在回调里写if/else
有了Payload,接收端就不需要在回调里写一堆if了。
我们做一个BusinessDispatcher——业务分发器。
它维护一个映射表:action对应哪个处理器。
接收回调只做一件事:解析Payload,然后调dispatcher.dispatch(payload)。
具体怎么处理?dispatcher自己找对应的处理器。
这段代码解决什么问题: 业务指令分发。
文件: dispatcher/BusinessDispatcher.ets
用途: 根据action分发到不同处理器
接入位置: 接收数据后调用
class BusinessDispatcher {
private handlers: Map<CrossAction, (payload: CrossDevicePayload) => void> = new Map();
// 注册处理器
register(action: CrossAction, handler: (payload: CrossDevicePayload) => void) {
this.handlers.set(action, handler);
}
// 分发
dispatch(payload: CrossDevicePayload) {
let handler = this.handlers.get(payload.action);
if (handler) {
console.info('执行动作:' + payload.action);
handler(payload);
} else {
console.warn('未知动作:' + payload.action);
}
}
}
// 使用
let dispatcher = new BusinessDispatcher();
dispatcher.register(CrossAction.ADD_TO_MATERIAL, (payload) => {
materialService.addResource(payload.uri);
});
dispatcher.register(CrossAction.INSERT_CANVAS, (payload) => {
canvasService.insertImage(payload.uri, payload.params.x, payload.params.y);
});
dispatcher.register(CrossAction.REPLACE_PREVIEW, (payload) => {
previewService.setSource(payload.uri);
});
四、为什么要这么分?
你可能会问:我直接在回调里写if/else不也能跑吗?
能跑,但有三个问题:
第一个问题:耦合。
传输逻辑和业务逻辑绑死了。你以后换个传输方式(比如从碰一碰换成WiFi直连),还得把业务代码抄一遍。
第二个问题:扩展。
加新动作,就得改接收回调。回调越改越大,最后变成几千行的大函数。
第三个问题:测试。
业务处理器是独立函数,可以单独测试。混在回调里,你得模拟整个传输流程才能测业务逻辑。
分开以后,传输层只管传数据,业务层只管处理数据,各干各的。


第五篇总结:
跨设备分享协议和业务协议应该解耦。
传输层只负责把数据传过来,业务层根据action字段分发到不同处理器。不要把所有if/else堆在接收回调里,那样业务一多就乱套了。
工程上最容易翻车的是把"传文件"和"怎么处理文件"混在一起。分开以后,加新动作只需要注册一个新处理器,不用动传输逻辑。
下一篇升级到多设备并发:一台手机同时发给多台设备,怎么保证互不影响。
更多推荐

所有评论(0)