杰理 SDK 文档中心
首页
首页
  • 概述与快速开始

    • 项目简介与功能总览
    • 运行环境与快速开始
    • 工程结构与文档布局
  • 架构与核心机制

    • 插件架构与原生平台桥接
    • 基类管理器与常量体系
    • 事件流与接收通知机制
  • 蓝牙连接与设备管理

    • 蓝牙连接与状态管理
    • 设备信息、配置与按键设置
    • 双设备连接与多链路管理
    • 数据传输与自定义命令
  • 音乐与媒体控制

    • 设备音乐与手机音乐播放控制
    • 音量与音频输出管理
  • 音效与音频模式

    • 均衡器与音效调节
    • 音频模式与降噪(ANC)设置
    • Auracast 音频广播
  • 设备功能控制

    • 闹钟管理
    • FM 收音机控制
    • 灯光控制
    • 充电仓与彩屏仓管理
  • 示例应用:杰理之家 Demo

    • 应用框架与交互组件
    • 设置、多语言与调试
  • 接口参考与文档中心

    • 发送接口参考
    • 接收接口与事件参考
    • 官方文档与集成指南

数据传输与自定义命令

本文档介绍 Flutter-JL_Home SDK 中与设备进行自定义数据收发相关的完整能力:通过 BleCustomCmdManager 向已连接的蓝牙设备发送自定义字节数据,通过 BleCustomCmdProcessor 与 BleEventStream 接收设备回传的自定义数据,以及官方示例应用 CustomCmdPage 中完整的 HEX/ASCII/UTF-8 编解码交互实现。

Purpose and Scope

本页覆盖「数据传输与自定义命令」这一能力的端到端实现:

  • 发送路径:BleCustomCmdManager.sendCustomCommand → BleBaseManager.invokeMethod → 原生层 sendCustomCommand 方法通道;
  • 接收路径:原生事件 → BleBaseEventProcessor 过滤 → BleCustomCmdProcessor.customCommandData → 对外统一流 BleEventStream.customCommandData;
  • 事件与方法常量:BleMethodConstants.methodSendCustomCommand、BleMethodConstants.argCustomData、BleEventConstants.typeCustomDataUpdate、BleEventConstants.keyCustomData;
  • 示例应用:CustomCmdPage 如何把文本编码为 UTF-8 字节、以 HEX/ASCII/UTF-8 三种视图预览数据,并订阅接收流。

与自定义命令无关的兄弟能力(如设备扫描连接、OTA 升级、音量控制、消息推送等)分别属于各自的目录页,本页不展开。SDK 顶层 API 入口(BleBaseManager、BleBaseEventProcessor、BleEventStream)仅在本页作为被调用的基础设施说明,其完整机制见对应文档。

Overview

JL(杰理)芯片生态的 BLE 设备除了标准服务外,通常还开放一条透传通道:App 可以把任意字节数组(如指令码、配置参数、私有协议帧)写入设备,设备也会异步回传自定义数据。SDK 为此提供了一对对称接口:

  • 发送:BleCustomCmdManager.sendCustomCommand(Uint8List data) —— 静态方法,将 Uint8List 通过 MethodChannel 交给原生层写入设备;
  • 接收:BleEventStream.customCommandData —— 静态 Stream,订阅后即可持续收到设备回传的 Uint8List 数据帧。

设计上的关键决策是对称性与解耦:

  1. 发送与接收都基于 Uint8List 原始字节,不规定业务协议——上层(App)自行决定如何组帧与解析,SDK 保持协议无关;
  2. 接收端采用「事件总线 + 类型过滤 + 流映射」模式:原生层把所有事件汇入统一事件流,BleBaseEventProcessor.filterByType 只挑出 typeCustomDataUpdate 类型的事件,再通过 getValueFromEvent 取出载荷并转换为 Uint8List;
  3. 对外暴露统一的门面类 BleEventStream,App 不必感知事件内部结构。

典型使用场景:私有指令下发(如查询固件信息、设置参数)、透传调试(HEX 指令工具)、厂商自定义协议的数据交换。

Architecture

flowchart TD
    subgraph sg_App["应用层 (example)"]
        CustomCmdPage["CustomCmdPage<br/>HEX/ASCII/UTF-8 预览 + 发送"]
    end

    subgraph sg_Sdk["SDK 层 (jl_home)"]
        SendMgr["BleCustomCmdManager<br/>sendCustomCommand"]
        EventStream["BleEventStream<br/>customCommandData"]
        CustomProc["BleCustomCmdProcessor<br/>filter + map"]
        BaseMgr["BleBaseManager<br/>invokeMethod"]
        BaseProc["BleBaseEventProcessor<br/>filterByType / getValueFromEvent"]
        MethodConst["BleMethodConstants<br/>methodSendCustomCommand / argCustomData"]
        EventConst["BleEventConstants<br/>typeCustomDataUpdate / keyCustomData"]
    end

    subgraph sg_Native["原生层 (Android/iOS)"]
        Channel["MethodChannel / EventChannel"]
        Device["BLE 设备"]
    end

    CustomCmdPage -->|"sendCustomCommand(Uint8List)"| SendMgr
    SendMgr -->|"invokeMethod"| BaseMgr
    BaseMgr -->|"方法名 + 参数"| MethodConst
    BaseMgr -->|"MethodChannel"| Channel
    Channel -->|"写入透传通道"| Device

    Device -->|"回传数据事件"| Channel
    Channel -->|"统一事件流"| BaseProc
    BaseProc -->|"filterByType(typeCustomDataUpdate)"| EventConst
    BaseProc -->|"getValueFromEvent → keyCustomData"| CustomProc
    CustomProc -->|"Stream&lt;Uint8List&gt;"| EventStream
    EventStream -->|"订阅"| CustomCmdPage

架构说明

  • 发送侧是一条单向调用链:页面 → 管理器 → 基础管理器 → 原生通道 → 设备。BleCustomCmdManager 是薄封装,职责仅是拼装方法名与参数并委托给 BleBaseManager.invokeMethod,真正的平台桥接发生在原生层。
  • 接收侧是一条反向的数据流:设备 → 原生通道 → 统一事件流 → 类型过滤 → 载荷提取 → 字节转换 → 对外 Stream。BleCustomCmdProcessor 是纯 Dart 的转换器,不接触平台通道。
  • 常量类(BleMethodConstants / BleEventConstants)是发送与接收两侧的契约:方法名 sendCustomCommand 与参数键 argCustomData 构成发送协议,事件类型 typeCustomDataUpdate 与载荷键 keyCustomData 构成接收协议。两端必须与原生层保持一致,是防止「字符串魔法值」散落各处的关键设计。

发送路径:BleCustomCmdManager

发送侧的核心实现非常精简——BleCustomCmdManager 是典型的门面(Facade)+ 静态工具类,把复杂的平台桥接隐藏在一个方法后面:

class BleCustomCmdManager {
  static Future<void> sendCustomCommand(Uint8List data) async {
    await BleBaseManager.invokeMethod(
      BleMethodConstants.methodSendCustomCommand,
      arguments: {BleMethodConstants.argCustomData: data},
    );
  }
}

Source: ble_custom_cmd_manager.dart

关键设计点

  1. 静态方法,无状态:整个类只有一个静态方法,不维护任何实例状态。这意味着发送动作与连接状态、页面生命周期完全解耦——任何地方(页面、后台任务、定时器)都可以直接调用。
  2. Uint8List 作为统一载荷类型:data 直接作为 MethodChannel 参数传入。Flutter 的 Uint8List 会被自动编码为原生层的字节数组(Android 为 ByteArray,iOS 为 NSData),无需手动序列化。
  3. 委托给 BleBaseManager.invokeMethod:SDK 内所有平台方法调用都收敛到这一个基础方法,统一处理 MethodChannel 的创建、异常抛出与日志,避免每个管理器重复样板代码。
  4. 契约集中在常量类:方法名 sendCustomCommand 与参数键 argCustomData 定义在 BleMethodConstants 中:
/// 发送自定义命令
static const String methodSendCustomCommand = "sendCustomCommand";

Source: ble_method_constants.dart

这样做的意义在于:原生侧(Android/iOS)只要按同一常量实现 sendCustomCommand 方法并读取 argCustomData 键,Dart 侧与原生侧就不会因字符串拼写不一致而静默失败。

发送时序

  1. App 构造 Uint8List(例如把文本 utf8.encode(...) 或把 HEX 字符串解析为字节);
  2. 调用 BleCustomCmdManager.sendCustomCommand(data);
  3. BleBaseManager.invokeMethod 通过 MethodChannel 调用原生方法 sendCustomCommand,参数 {argCustomData: data};
  4. 原生层把字节写入 BLE 透传特征(写入响应由原生层内部处理);
  5. Future 完成即代表方法调用已被原生层接收(注意:并不代表设备已确认收到——确认语义由上层协议自行定义)。

接收路径:BleCustomCmdProcessor

接收侧采用「统一事件流 + 类型过滤 + 转换映射」的管道式设计:

/// Ble custom cmd processor
class BleCustomCmdProcessor{

  static Stream<Uint8List> get customCommandData {
    return BleBaseEventProcessor.filterByType(
      BleEventConstants.typeCustomDataUpdate,
    ).map((event) {
      try {
        final data = BleBaseEventProcessor.getValueFromEvent(event);

        final customData = data[BleEventConstants.keyCustomData];

        if (customData == null) {
          return Uint8List(0);
        }

        if (customData is List) {
          if (customData is List<int>) {
            return Uint8List.fromList(customData);
          }

          final List<int> result = [];
          for (var element in customData) {
            if (element is int) {
              result.add(element);
            } else if (element is num) {
              result.add(element.toInt());
            } else {
              return Uint8List(0);
            }
          }
          return Uint8List.fromList(result);
        }
        return Uint8List(0);
      } catch (e) {
        return Uint8List(0);
      }
    });
  }
}

Source: ble_custom_cmd_processor.dart

管道各阶段详解

阶段实现职责
1. 事件过滤BleBaseEventProcessor.filterByType(BleEventConstants.typeCustomDataUpdate)从全局事件流中只保留自定义数据更新事件,其他类型事件(音量、OTA、消息推送等)全部丢弃
2. 载荷提取BleBaseEventProcessor.getValueFromEvent(event)把事件对象解包为 Map,得到携带业务数据的字典
3. 取值data[BleEventConstants.keyCustomData]按契约键取出原始数据字段
4. 类型归一分支处理 List / List<int> / 非 int 元素把原生层可能返回的各种列表形态统一转换为 Uint8List
5. 容错null、非法元素、异常均返回 Uint8List(0)保证 Stream 永不因单帧脏数据而中断

为什么返回空数组而不是抛异常?

这是接收管道最重要的设计决策:订阅方拿到 Uint8List(0) 比收到异常更安全。理由如下:

  • 自定义数据是高频、异步、不可控的外部输入,任何一帧数据解析失败都不应该影响后续帧;
  • Stream 在 map 内抛出异常会导致整个订阅链断开,App 需要重新订阅才能继续接收,这是不可接受的脆弱性;
  • 空数组是明确的「无有效载荷」信号,订阅方可以用 data.isEmpty 快速判断并跳过。

代价是错误被静默吞掉:如果原生层长期返回格式不符的数据,问题难以定位。因此该设计依赖原生层严格遵守 typeCustomDataUpdate / keyCustomData 契约,调试时建议在原生侧先行校验。

对外门面:BleEventStream

SDK 把该 Stream 汇总到统一门面类 BleEventStream 中,与所有其他设备事件流并列:

// Get custom data
static Stream<Uint8List> get customCommandData =>
    BleCustomCmdProcessor.customCommandData;

Source: ble_event_stream.dart

这保证了 API 一致性:App 无论订阅音量、电量还是自定义数据,都从同一个 BleEventStream 静态属性获取,学习成本低,也便于在门面层统一添加日志或调试能力。

Core Flow:一次完整的数据收发

sequenceDiagram
    participant Page as CustomCmdPage
    participant Mgr as BleCustomCmdManager
    participant Base as BleBaseManager
    participant Native as 原生层 (MethodChannel)
    participant Dev as BLE 设备
    participant Proc as BleCustomCmdProcessor
    participant Stream as BleEventStream.customCommandData

    Note over Page: initState 中订阅接收流
    Page->>Stream: listen((data) => setState(...))
    Stream-->>Page: 订阅建立

    Note over Page: 用户点击发送
    Page->>Page: utf8.encode(text) → Uint8List
    Page->>Mgr: sendCustomCommand(data)
    Mgr->>Base: invokeMethod("sendCustomCommand", {argCustomData: data})
    Base->>Native: MethodChannel 调用
    Native->>Dev: 写入透传特征
    Native-->>Base: 方法调用返回
    Base-->>Mgr: Future 完成
    Mgr-->>Page: sendCustomCommand 返回

    Note over Dev: 设备异步回传数据
    Dev-->>Native: 透传特征通知
    Native-->>Proc: 事件 typeCustomDataUpdate
    Proc->>Proc: filterByType + getValueFromEvent + 转 Uint8List
    Proc-->>Stream: Uint8List 数据帧
    Stream-->>Page: onData 回调
    Page->>Page: HEX / ASCII / UTF-8 三种视图展示

时序要点

  1. 订阅先于发送:页面在 initState 阶段订阅 customCommandData,保证设备回传的第一帧数据也不会丢失;
  2. 发送是异步但不等设备确认:sendCustomCommand 的 Future 只表示原生方法调用已发出,回传数据完全由接收流异步送达,两者通过 Uint8List 字节流间接关联(无请求-响应配对,属于异步消息模型);
  3. 回传经过纯 Dart 管道:从原生事件到页面回调之间没有任何平台通道参与,全部由 BleBaseEventProcessor 与 BleCustomCmdProcessor 完成,便于单元测试。

示例应用:CustomCmdPage

官方示例中的 CustomCmdPage 是自定义命令能力的参考实现,展示了完整的交互闭环:文本输入 → UTF-8 编码 → 预览 → 发送 → 接收 → 三种视图解码展示。

页面状态与订阅

/// Custom command page for sending and receiving data
class CustomCmdPage extends StatefulWidget {
  const CustomCmdPage({super.key});

  @override
  State<CustomCmdPage> createState() => _CustomCmdPageState();
}

class _CustomCmdPageState extends State<CustomCmdPage> {
  ...
  Uint8List? _receivedData;
  StreamSubscription<Uint8List>? _dataSubscription;
  final TextEditingController _textController = TextEditingController();
  final FocusNode _textFocusNode = FocusNode();

Source: custom_cmd_page.dart 与 custom_cmd_page.dart

关键点:

  • _dataSubscription 持有订阅句柄,页面销毁时(dispose)必须取消,防止内存泄漏与回调访问已销毁的 State;
  • _receivedData 是 Uint8List?——null 表示「尚未收到任何数据」,与「收到一帧空数据」在 UI 上区分开(_noDataReceivedText = 'No data received yet');
  • 输入框用 TextEditingController 管理,配合 FocusNode 在点击发送时收起键盘(FocusScope.of(context).unfocus())。

发送按钮回调

      FocusScope.of(context).unfocus();
      _sendCustomCmdToDevice();
    };

Source: custom_cmd_page.dart

页面把用户输入的文本按 UTF-8 编码为 Uint8List,再调用 BleCustomCmdManager.sendCustomCommand,并在成功后通过 SnackBar 提示 Sent N bytes successfully;若输入为空则提示 Please enter some text,编码失败则提示 Failed to convert text to bytes。这些文案常量集中在页面顶部(_sentSuccessPrefix、_pleaseEnterTextMessage、_failedToConvertMessage 等),便于后续国际化替换——事实上示例工程已通过 l10n 生成了多语言版本(如日文的 sendCustomCmd => 'カスタムコマンドを送信する')。

数据展示:HEX / ASCII / UTF-8 三视图

接收到的 Uint8List 在 UI 上以三种视角呈现,这是调试透传数据的标准做法:

视图颜色常量逻辑用途
HEX_hexDataColor (blueGrey)每字节格式化为两位大写十六进制查看原始帧结构、校验位
ASCII_asciiDataColor (green)可打印范围(32–126)直接显示字符,其余显示 [Non-ASCII characters]快速阅读可读文本
UTF-8_utf8DataColor (orange)utf8.decode,失败显示 [UTF-8 decode error]查看中文字符串等多字节文本

ASCII 范围常量 _asciiMinChar = 32、_asciiMaxChar = 126 定义了可打印区间;HEX 填充常量 _hexPadLeftLength = 2、_hexPadLeftChar = '0' 保证每字节固定两位输出。三种视图共用一份 Uint8List,互不干扰,体现了「原始字节单一数据源 + 多视图派生」的 UI 模式。

Usage Examples

基本用法:发送文本数据

将用户输入编码为 UTF-8 字节并发送(示例页面的核心交互):

final Uint8List data = Uint8List.fromList(utf8.encode(text));
await BleCustomCmdManager.sendCustomCommand(data);

Source: custom_cmd_page.dart(dart:convert 的 utf8 与 dart:typed_data 的 Uint8List 导入)

基本用法:订阅接收流

在 initState 中订阅,dispose 中取消——这是推荐的生命周期管理模式:

StreamSubscription<Uint8List>? _dataSubscription;

@override
void initState() {
  super.initState();
  _dataSubscription = BleEventStream.customCommandData.listen((data) {
    if (!mounted) return;
    setState(() => _receivedData = data);
  });
}

@override
void dispose() {
  _dataSubscription?.cancel();
  super.dispose();
}

Sources:

  • ble_event_stream.dart(流定义)
  • custom_cmd_page.dart(订阅句柄模式)

高级用法:发送自定义二进制指令

发送任意原始字节(例如私有协议帧 0xAA 0x55 0x01 0x00 0x02):

final Uint8List frame = Uint8List.fromList([0xAA, 0x55, 0x01, 0x00, 0x02]);
await BleCustomCmdManager.sendCustomCommand(frame);

Source: ble_custom_cmd_manager.dart(方法签名即 Uint8List 入参,天然支持二进制)

Configuration Options(协议常量)

SDK 通过常量类集中管理发送/接收两侧的协议契约。这些常量必须与原生层实现保持一致,属于协议配置而非运行时可调参数:

常量值(按源码注释/命名推断)所属文件用途
BleMethodConstants.methodSendCustomCommand"sendCustomCommand"ble_method_constants.dartMethodChannel 方法名:发送自定义命令
BleMethodConstants.argCustomDataargCustomData(键名)ble_method_constants.dart发送方法参数键:携带 Uint8List 载荷
BleEventConstants.typeCustomDataUpdatetypeCustomDataUpdate(事件类型)ble_event_constants.dart事件类型:自定义数据更新
BleEventConstants.keyCustomDatakeyCustomData(事件载荷键)ble_event_constants.dart事件载荷键:取出自定义数据字段

说明:argCustomData、typeCustomDataUpdate、keyCustomData 的精确字符串值定义于对应常量类中,本页通过它们在 ble_custom_cmd_manager.dart 与 ble_custom_cmd_processor.dart 中的使用位置加以确认。

API Reference

BleCustomCmdManager.sendCustomCommand(Uint8List data): Future<void>

发送自定义命令数据到当前连接的设备。

参数:

  • data (Uint8List):要发送的原始字节载荷。可以是文本的 UTF-8 编码,也可以是任意二进制帧。

返回: Future<void>,在 MethodChannel 方法调用完成时完成。注意该 Future 只表示调用已送达原生层,不保证设备已消费数据。

抛出:

  • PlatformException:当设备未连接或原生层发送失败时,由 BleBaseManager.invokeMethod 向上传播(发送前建议先确认连接状态)。

源码: ble_custom_cmd_manager.dart

BleCustomCmdProcessor.customCommandData: Stream<Uint8List>

自定义数据接收流(处理器层)。

返回: Stream<Uint8List>。过滤 typeCustomDataUpdate 类型事件并转换为 Uint8List;解析失败或载荷为空时产出 Uint8List(0),不会抛出异常或中断流。

源码: ble_custom_cmd_processor.dart

BleEventStream.customCommandData: Stream<Uint8List>

对外统一门面属性,等价于 BleCustomCmdProcessor.customCommandData,App 应优先使用本入口以保持 API 一致性。

源码: ble_event_stream.dart

Failure Modes, Edge Cases & Concurrency

1. 接收数据的类型与值异常

处理器对回传载荷做了多层防御(ble_custom_cmd_processor.dart):

场景行为说明
customData == null返回 Uint8List(0)原生层事件缺少载荷键
载荷是 List<int>Uint8List.fromList 直接转换最常见的正常路径
载荷是含 num 元素的 List逐个 toInt() 归一兼容原生层返回 double 型字节
载荷含非数值元素返回 Uint8List(0)整帧视为无效
载荷不是 List返回 Uint8List(0)类型契约被破坏
转换过程抛异常catch (e) 返回 Uint8List(0)兜底,保证流不断

边界语义:Uint8List(0) 既是错误哨兵也是合法空帧,订阅方若需区分「错误」与「空数据」,应在协议层定义(例如约定帧头+长度校验),SDK 层刻意不做业务判定。

2. 发送失败与连接状态

sendCustomCommand 依赖当前 BLE 连接。若设备未连接或连接已断开,原生层会抛出 PlatformException。示例页面的 ConnectionStateManager(connection_state_manager.dart)专门维护连接状态,页面据此决定发送按钮可用性(_sendButtonDisabledColor)。最佳实践:发送前检查连接状态,并对 PlatformException 做 try/catch 或 catchError 处理,避免未捕获异常导致红屏。

3. 无请求-响应配对(异步消息模型)

发送与接收是两条独立的异步通道:一次 sendCustomCommand 调用后,设备可能回传零帧、一帧或多帧数据,SDK 不做关联。并发/一致性关注点:

  • 高频连续发送时,回传帧的顺序由设备端决定,SDK 不保证与发送顺序一一对应;
  • 若业务需要「请求→响应」配对(如查询指令),上层需在载荷中自行加入帧序号/事务 ID,并在订阅回调中匹配;
  • 多个订阅者同时 listen 同一 BleEventStream.customCommandData 时,各自独立收到完整事件流(取决于底层 BleBaseEventProcessor 的广播实现),上层不应假设「一人消费、他人不消费」。

4. 订阅生命周期

页面未取消订阅会导致:内存泄漏、setState 在 dispose 后调用(示例页面用 if (!mounted) return; 防御)、回调触发已销毁的 State。这是 Flutter 异步编程的经典坑,示例代码给出了正确范式。

5. 空帧/噪声数据的 UI 呈现

_receivedData 为 null 与 Uint8List(0) 在 UI 上分别显示「No data received yet」与空数据区。HEX 视图中空数组显示为空字符串而非错误,避免干扰调试。

Performance & Operational Notes

  • 载荷编码开销:Uint8List.fromList 与 utf8.encode 均为 O(n) 操作,对典型指令帧(几十到几百字节)开销可忽略;应避免在回调中对大数据帧做重复解码(示例页面只解码一次,三种视图共享字节源)。
  • 流的恒常性:接收管道是纯 Dart 转换,无平台通道往返,吞吐瓶颈在原生 BLE 层(MTU 大小、连接间隔),而非本页代码路径。
  • 调试建议:先使用 CustomCmdPage 的 HEX 视图验证帧结构,再编写上层协议解析;若长期收不到数据,优先检查原生层是否发出了 typeCustomDataUpdate 事件(可临时在 map 中打印 event 调试)。
  • 错误静默的运维代价:处理器吞掉所有解析异常,线上问题难以直接观测。如有需要,可在 catch (e) 分支加入日志埋点(SDK 当前版本未内置,属于扩展点)。

Extension Points

  1. 协议无关的载荷模型:SDK 只传输 Uint8List,上层可以自由定义帧格式(帧头/长度/CRC/序号)。这是本能力最大的扩展空间——例如在 CustomCmdPage 基础上扩展为指令面板、脚本编辑器等。
  2. 自定义解析器:可以在订阅 customCommandData 后再 map 一层,把 Uint8List 解析为业务模型(如 DeviceInfo、ConfigAck),保持页面层只面向强类型对象。
  3. 新增命令方法:若需要除 sendCustomCommand 外的专用方法(如带确认回调的 sendCommandWithAck),可在 BleCustomCmdManager 旁新增管理器,并沿用 BleBaseManager.invokeMethod + 常量类契约模式,与原生层协商新方法名。
  4. 多语言/UI 定制:示例页面将全部文案收敛为页面常量并接入 l10n,便于复制改造为生产级工具页。

Tests

示例工程包含集成测试入口 plugin_integration_test.dart,用于验证插件与真实/模拟设备的数据通路。由于自定义命令依赖实际 BLE 连接,纯 Dart 侧建议针对以下逻辑补充单元测试:

  • BleCustomCmdProcessor 的 map 转换:构造含 List<int>、List<num>、null、非法元素的伪事件,断言输出 Uint8List 与空数组分支;
  • 页面状态机的收发闭环(可借助 StreamController 模拟 customCommandData)。

说明:处理器与页面为静态/实例逻辑,但示例仓库中未发现针对自定义命令管道的独立单元测试文件;上述建议基于源码中可测的纯函数结构给出,属测试覆盖缺口,可作贡献点。

Related Links

  • BleCustomCmdManager(发送接口)
  • BleCustomCmdProcessor(接收处理器)
  • BleEventStream(事件流门面)
  • BleMethodConstants(方法常量)
  • CustomCmdPage(示例页面)
  • 相关目录页:设备连接管理(BleBaseManager / BleBaseEventProcessor 基础设施)、OTA 升级、设备控制(音量/消息推送等事件流的用法与本页接收模式一致)。
Prev
双设备连接与多链路管理