前面都是传文件,这次升级一下:手机碰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直连),还得把业务代码抄一遍。

第二个问题:扩展。
加新动作,就得改接收回调。回调越改越大,最后变成几千行的大函数。

第三个问题:测试。
业务处理器是独立函数,可以单独测试。混在回调里,你得模拟整个传输流程才能测业务逻辑。

分开以后,传输层只管传数据,业务层只管处理数据,各干各的。

手机端分享选项

PC端插入效果


第五篇总结:

跨设备分享协议和业务协议应该解耦。

传输层只负责把数据传过来,业务层根据action字段分发到不同处理器。不要把所有if/else堆在接收回调里,那样业务一多就乱套了。

工程上最容易翻车的是把"传文件"和"怎么处理文件"混在一起。分开以后,加新动作只需要注册一个新处理器,不用动传输逻辑。

下一篇升级到多设备并发:一台手机同时发给多台设备,怎么保证互不影响。

Logo

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

更多推荐