自定义命令扩展
自定义命令扩展(Custom Command)是 JL RCSP SDK 提供的一种"透传"机制,允许 App 与设备(手表/手环等)之间直接交换业务自定义的原始字节数据,用于支持 SDK 未内置的客户拓展功能。
Purpose and Scope
本页面说明如何在 JL Health SDK 体系下开发自定义命令(Custom Command):包括命令的构建、发送、接收、回复、状态判定、MTU 大小限制以及错误处理。页面以仓库中两个官方 Demo —— com.jieli.watchtesttool.CustomCommandDemo 与 com.jieli.healthaide.demos.CustomCommandDemo —— 为唯一事实来源,完整解读其用法与设计意图。
本页面不涉及以下内容(属于其它目录页范畴):
- SDK 的蓝牙连接、扫描、配对流程(见"连接与配对"相关页面);
- SDK 内置业务命令(OTA、健康数据、表盘管理等,各功能有独立页面);
- 协议栈底层封包格式与 RCSP 帧结构细节。
Overview
为什么需要自定义命令
SDK 内置了大量标准功能(OTA 升级、健康数据同步、闹钟、表盘管理等),但客户产品的私有功能无法全部预置。自定义命令提供一条通用通道:
- 下行(App → 设备):App 把任意字节数组打包成
CustomCmd命令发送给设备,设备解析后回复执行结果; - 上行(设备 → App):设备主动下发自定义命令,App 通过全局回调接收并处理,可选择性回复。
这一机制使客户无需修改 SDK 即可扩展任意功能,且数据载荷(byte[])完全由业务方定义,与固件侧协商好格式即可。
关键概念
| 概念 | 说明 |
|---|---|
CustomCmd | 自定义命令的模型类(com.jieli.jl_rcsp.model.command.custom.CustomCmd),承载参数与响应 |
CustomParam | 命令参数,getData() 返回设备下发或 App 上行的自定义字节数据 |
CustomResponse | 命令响应,getData() 返回设备回复的自定义字节数据 |
CommandBuilder.buildCustomCmd(data) | 构建一条需要设备回复的自定义命令 |
CommandBuilder.buildCustomCmdWithoutResponse(data) | 构建一条无需设备回复的自定义命令(单向通知) |
Command.CMD_CUSTOM / Command.CMD_EXTRA_CUSTOM | 命令 ID,用于在回调中过滤自定义命令 |
StateCode.STATUS_SUCCESS 等 | 命令执行状态码 |
RcspErrorCode | 错误码集合,用于构造 BaseError |
两个 Demo 的差异
仓库中存在两份 CustomCommandDemo:
- WatchTestTool 版(
code/tool/WatchTestTool_V0.9.0_SDK_V1.14.0/.../watchtesttool/CustomCommandDemo.java)是最简用法,README 明确指定其为参考实现;发送时固定使用Command.CMD_CUSTOM过滤,命令均需回复。 - HealthAide 版(
code/app/HealthAide_V1.1.0_SDK_V1.14.0/.../healthaide/demos/CustomCommandDemo.java)是增强版:支持"无回复"构建模式、按命令类型(FLAG_HAVE_PARAMETER_AND_RESPONSE等)动态判断是否需要回复、细化STATUS_UNKOWN_CMD错误分支、并给出 MTU 计算示例;接收时使用Command.CMD_EXTRA_CUSTOM过滤。
两者底层都依赖 WatchManager(WatchOpImpl 的子类封装)与 com.jieli.jl_rcsp SDK,掌握其一即可迁移到另一工程。
Architecture
自定义命令扩展在 App 侧的完整链路如下图所示:业务层通过 WatchManager(SDK 门面)与 CommandBuilder(命令工厂)交互,底层由 RCSP 协议栈负责蓝牙传输,设备侧解析后通过回调回流到业务层。
flowchart TD
subgraph sg_Biz["业务层 (Demo) com.jieli.*.demos / watchtesttool"]
Send["CustomCommandDemo.sendCustomCommand()"]
Recv["CustomCommandDemo.receiveCustomCmd()"]
MTU["CustomCommandDemo.checkRCSPProtocolMTU()"]
end
subgraph sg_App["App 封装层"]
WM["WatchManager (WatchOpImpl 子类)"]
SendRcsp["sendRcspCommand()"]
SendResp["sendCommandResponse()"]
RegCb["registerOnRcspCallback()"]
Target["getTargetDevice()"]
end
subgraph sg_Sdk["jl_rcsp SDK 层"]
Builder["CommandBuilder"]
Build1["buildCustomCmd(data)"]
Build2["buildCustomCmdWithoutResponse(data)"]
Model["CustomCmd / CustomParam / CustomResponse"]
CB["OnRcspCallback.onRcspCommand()"]
CBErr["RcspCommandCallback.onCommandResponse() / onErrCode()"]
DSM["DeviceStatusManager.getMaxReceiveMtu()"]
State["StateCode / RcspErrorCode"]
end
subgraph sg_Dev["设备 (固件)"]
Dev["手环/手表固件"]
end
Send -->|"获取实例"| WM
Send -->|"构建命令"| Builder
Builder --> Build1
Builder --> Build2
Build1 --> Model
Build2 --> Model
Send -->|"发送并等待回调"| SendRcsp
SendRcsp -->|"携带 CustomCmd"| Target
SendRcsp --> CBErr
Recv --> RegCb
RegCb --> CB
CB -->|"按命令 ID 过滤后强转"| Model
Recv -->|"回复结果"| SendResp
SendResp --> Model
MTU --> DSM
DSM -->|"protocolMtu - 23"| Send
SendRcsp -->|"BLE 透传"| Dev
Dev -->|"设备主动下发"| CB
CBErr -->|"BaseError"| Send
State -.->|"状态/错误判定"| CBErr
State -.-> CB
架构解读:
- 业务层是唯一的客户代码入口:三个方法分别覆盖"发送 + 接收 + 尺寸约束"三个扩展维度,客户只需实现
sendCustomCommand(byte[])与receiveCustomCmd()中的解析逻辑。 - App 封装层
WatchManager屏蔽了设备管理细节:getInstance()单例获取、getTargetDevice()得到当前连接设备、sendRcspCommand()统一发送入口、sendCommandResponse()回复设备、registerOnRcspCallback()注册全局上行命令回调。Demo 注释强调"WatchManager 是 WatchOpImpl 的子类,须在 SDK 配置好之后使用",即连接建立后才能发送命令。 - SDK 层
CommandBuilder是命令工厂:buildCustomCmd/buildCustomCmdWithoutResponse分别生成"要回复"与"不要回复"的命令;CustomCmd内部按 RCSP 协议打包,CustomParam/CustomResponse承载业务字节。 - 设备侧是自定义命令的另一端:下行命令由固件解析并回复状态;上行命令由固件主动发起,App 侧
onRcspCommand回调统一收口。
核心机制详解
1. 发送自定义命令(App → 设备)
发送链路的完整步骤(以 HealthAide 增强版为例,见 CustomCommandDemo.java):
- 获取管理器:
WatchManager.getInstance()取得全局单例(须先完成 SDK 初始化与设备连接配置)。 - 构建命令:
CommandBuilder.buildCustomCmd(data)生成携带自定义数据的命令;若业务不需要设备回复(如单向通知、心跳探活),改用buildCustomCmdWithoutResponse(data)—— 该模式下 SDK 不会进入等待响应的状态机,onCommandResponse回调在发送完成后即被触发(响应体为空)。 - 发送并等待回调:
manager.sendRcspCommand(manager.getTargetDevice(), customCmd, callback)。 - 状态判定:回调
onCommandResponse中首先检查cmd.getStatus():STATUS_SUCCESS→ 继续解析响应;STATUS_UNKOWN_CMD(设备不认识该命令)→ 构造BaseError(RcspErrorCode.ERR_FUNC_NOT_SUPPORT),并回填opCode与sn便于定位;- 其它非成功状态 → 通过
RcspErrorCode.buildJsonError(...)构造带状态码详情的错误。
- 响应解析:按命令类型判断是否需要响应——
FLAG_HAVE_PARAMETER_AND_RESPONSE或FLAG_NO_PARAMETER_HAVE_RESPONSE表示带回复;若类型声明有回复但cmd.getResponse()为null,判定为ERR_PARSE_DATA解析错误;否则response.getData()即设备回复的业务字节。
WatchTestTool 简版逻辑相同但更精简:只检查 STATUS_SUCCESS 与响应非空,其余状态统一走 ERR_RESPONSE_BAD_RESULT 分支。
2. 接收自定义命令(设备 → App)
接收链路(见 CustomCommandDemo.java):
- 注册全局回调:
manager.registerOnRcspCallback(new OnRcspCallback(){...})。该回调接收所有上行 RCSP 命令,因此必须按命令 ID 过滤。 - ID 过滤:
if (command.getId() != Command.CMD_EXTRA_CUSTOM) return;(WatchTestTool 版过滤Command.CMD_CUSTOM)。注意两个 Demo 使用了不同的命令 ID 常量——这是客户与固件协商的结果,实际项目应统一为同一个 ID,避免过滤失效。 - 强转模型:
CustomCmd customCmd = (CustomCmd) command;,随后通过customCmd.getType()判断该命令是否需要回复。 - 参数校验:
customCmd.getParam()为null时说明数据非法:- 若该命令需要回复 → 置
STATUS_SUCCESS(或按业务置失败)后调用manager.sendCommandResponse(device, customCmd, null)回执; - 否则直接返回。
- 若该命令需要回复 → 置
- 业务处理:
param.getData()取出设备下发的字节,进行业务解析;需要回复时,customCmd.setStatus(StateCode.STATUS_SUCCESS)并可通过customCmd.setParam(...)携带回复数据(Demo 中展示了被注释的responseData设置示例,默认以null回复空数据),最后sendCommandResponse回传。
3. 命令类型(是否需要回复)
CommandBase 定义了四种命令类型标志,Demo 中通过位判断决定行为:
| 类型标志 | 含义 |
|---|---|
FLAG_HAVE_PARAMETER_AND_RESPONSE | 有参数且需要响应(最常见的双向命令) |
FLAG_NO_PARAMETER_HAVE_RESPONSE | 无参数但需要响应 |
| 其它标志(无响应类) | 单向命令,发送后不等待回复 |
发送方据此决定是否读取 cmd.getResponse();接收方据此决定是否调用 sendCommandResponse()。设计意图:协议层将"是否需要回复"作为命令属性携带,使收发双方无需额外约定即可保持状态机一致,避免出现"发送方等待回复而设备不回复"导致的超时挂起。
4. MTU 与自定义数据大小限制
//最大发送MTU
int protocolMtu = DeviceStatusManager.getInstance().getMaxReceiveMtu(device);
//自定义命令的大小 = 最大发送MTU - 协议包长度
int customDataLimit = protocolMtu - 23;
//建议与固件协商好,不建议发最大数据
设计意图:BLE 单包 MTU 有限(常见 23~517 字节),RCSP 协议封装(命令头、参数区、校验等)约占 23 字节,因此自定义数据净荷上限约为 MTU - 23。Demo 明确建议:与固件协商确定实际分包/限长策略,且不要每次都发满上限——留出余量可避免极端情况下的封包失败或丢包重传,提升成功率。超过该长度的大数据应使用 SDK 的"大文件传输/分包"机制,而不是自定义命令。
Core Flow
下行:App 发送自定义命令并接收设备回复
sequenceDiagram
participant App as 业务层 (Demo)
participant WM as WatchManager
participant B as CommandBuilder
participant SDK as RCSP 协议栈
participant Dev as 设备固件
App->>WM: getInstance() / getTargetDevice()
App->>B: buildCustomCmd(data) 或 buildCustomCmdWithoutResponse(data)
B-->>App: CustomCmd
App->>WM: sendRcspCommand(device, customCmd, callback)
WM->>SDK: 打包并发送 (BLE)
SDK->>Dev: 自定义命令帧
alt 需要回复的命令
Dev-->>SDK: 回复状态 + CustomResponse 数据
SDK-->>WM: 分发响应
WM-->>App: onCommandResponse(device, cmd)
App->>App: 校验 status == STATUS_SUCCESS
App->>App: 校验 response != null → getData() 解析
else 无需回复的命令
Dev-->>SDK: 仅回执状态(或无回执)
SDK-->>WM: 发送完成事件
WM-->>App: onCommandResponse(device, cmd) (无响应体)
end
Note over App: 失败时走 onErrCode(device, BaseError)
上行:设备主动下发自定义命令
sequenceDiagram
participant Dev as 设备固件
participant SDK as RCSP 协议栈
participant WM as WatchManager
participant CB as OnRcspCallback
participant App as 业务层 (Demo)
Dev->>SDK: 上行自定义命令帧
SDK->>WM: 解析为 CommandBase
WM->>CB: onRcspCommand(device, command)
CB->>CB: getId() != CMD_CUSTOM/CMD_EXTRA_CUSTOM → return
CB->>CB: 强转 CustomCmd, 判断 type 是否需要回复
alt param == null
CB->>WM: sendCommandResponse(device, cmd, null) 回执(若需回复)
else param != null
CB->>App: param.getData() 业务解析
CB->>WM: setStatus(SUCCESS) + sendCommandResponse(device, cmd, null)
end
Usage Examples
示例 1:发送自定义命令(最简版,WatchTestTool)
该示例展示最直接的用法:构建命令 → 发送 → 回调中校验状态并解析响应。
@Test
public void sendCustomCommand(byte[] data) {
//WatchManager是WatchOpImpl的子类,须在1.3配置好sdk
WatchManager manager = WatchManager.getInstance();
//Send custom command and waiting for the result callback
manager.sendRcspCommand(manager.getTargetDevice(), CommandBuilder.buildCustomCmd(data), new RcspCommandCallback<CustomCmd>() {
@Override
public void onCommandResponse(BluetoothDevice device, CustomCmd cmd) {
if (cmd.getStatus() != StateCode.STATUS_SUCCESS) {
onErrCode(device, new BaseError(RcspErrorCode.ERR_RESPONSE_BAD_RESULT, "Device Reply an bad state: " + cmd.getStatus()));
return;
}
CustomResponse response = cmd.getResponse();
if (null == response) {
onErrCode(device, new BaseError(RcspErrorCode.ERR_PARSE_DATA, "Response data is error."));
return;
}
byte[] data = response.getData();
//parse data
}
@Override
public void onErrCode(BluetoothDevice device, BaseError error) {
//callback error event
}
});
}
Source: CustomCommandDemo.java
示例 2:发送自定义命令(增强版,支持无回复模式与错误细分)
HealthAide 版将"是否需要回复"显式建模:可选用 buildCustomCmdWithoutResponse 构建单向命令,并根据命令类型标志决定是否解析响应体;同时把 STATUS_UNKOWN_CMD 单独映射为 ERR_FUNC_NOT_SUPPORT,便于产品层提示"设备不支持该功能"。
@Test
public void sendCustomCommand(byte[] data) {
//WatchManager是WatchOpImpl的子类,须在1.3配置好sdk
WatchManager manager = WatchManager.getInstance();
//build the custom command
CommandBase customCmd = CommandBuilder.buildCustomCmd(data); //carries custom data
final boolean isNoResponse = false; //Set whether the command does not require reply
if (isNoResponse) {
customCmd = CommandBuilder.buildCustomCmdWithoutResponse(data);//Setting does not require reply
}
//Send custom command and waiting for the result callback
manager.sendRcspCommand(manager.getTargetDevice(), customCmd, new RcspCommandCallback<CustomCmd>() {
@Override
public void onCommandResponse(BluetoothDevice device, CustomCmd cmd) {
final int status = cmd.getStatus();
if (status != StateCode.STATUS_SUCCESS) {
if (status == StateCode.STATUS_UNKOWN_CMD) {
onErrCode(device, new BaseError(RcspErrorCode.ERR_FUNC_NOT_SUPPORT)
.setOpCode(cmd.getId()).setSn(cmd.getOpCodeSn()));
return;
}
onErrCode(device, RcspErrorCode.buildJsonError(cmd.getId(), cmd.getOpCodeSn(), RcspErrorCode.ERR_RESPONSE_BAD_STATUS,
status, StateCode.printResponseStatus(status)));
return;
}
boolean hasResponse = cmd.getType() == CommandBase.FLAG_HAVE_PARAMETER_AND_RESPONSE
|| cmd.getType() == CommandBase.FLAG_NO_PARAMETER_HAVE_RESPONSE;
if (hasResponse) { //有回复
CustomResponse response = cmd.getResponse();
if (null == response) {
onErrCode(device, new BaseError(RcspErrorCode.ERR_PARSE_DATA, RcspErrorCode.getErrorDesc(RcspErrorCode.ERR_PARSE_DATA)));
return;
}
byte[] data = response.getData();
//parse data
}
}
@Override
public void onErrCode(BluetoothDevice device, BaseError error) {
//callback error event
}
});
}
Source: CustomCommandDemo.java
示例 3:接收设备下发的自定义命令
接收侧必须先 registerOnRcspCallback 注册全局回调,然后在回调内按命令 ID 过滤、强转模型、解析参数,并按需回复:
@Test
public void receiveCustomCmd() {
//WatchManager是WatchOpImpl的子类,须在1.3配置好sdk
WatchManager manager = WatchManager.getInstance();
//add Rcsp event callback
manager.registerOnRcspCallback(new OnRcspCallback() {
@Override
public void onRcspCommand(BluetoothDevice device, CommandBase command) {
//receive rcsp command
if (command.getId() != Command.CMD_EXTRA_CUSTOM) return; //filter other command
CustomCmd customCmd = (CustomCmd) command;
//Determine whether to reply the command
boolean hasResponse = customCmd.getType() == CommandBase.FLAG_HAVE_PARAMETER_AND_RESPONSE
|| customCmd.getType() == CommandBase.FLAG_NO_PARAMETER_HAVE_RESPONSE;
CustomParam param = customCmd.getParam();
if (param == null) {
if (hasResponse) {
customCmd.setParam(null);
customCmd.setStatus(StateCode.STATUS_SUCCESS);
manager.sendCommandResponse(device, customCmd, null);
}
return;
}
byte[] data = param.getData(); //the custom data from device
//parse data
if (hasResponse) { //需要回复
//doing some thing and reply a success result.
customCmd.setStatus(StateCode.STATUS_SUCCESS);
customCmd.setParam(null);
manager.sendCommandResponse(device, customCmd, null);
}
}
});
}
Source: CustomCommandDemo.java
WatchTestTool 简版的接收逻辑(过滤 Command.CMD_CUSTOM、参数为空时置 STATUS_FAIL 回复)见 CustomCommandDemo.java。
示例 4:计算自定义数据长度上限
@Test
public void checkRCSPProtocolMTU(BluetoothDevice device) {
//最大发送MTU
int protocolMtu = DeviceStatusManager.getInstance().getMaxReceiveMtu(device);
//自定义命令的大小 = 最大发送MTU - 协议包长度
int customDataLimit = protocolMtu - 23;
//建议与固件协商好,不建议发最大数据
}
Source: CustomCommandDemo.java
API Reference
以下 API 均来自 com.jieli.jl_rcsp SDK 与 WatchManager 封装,签名以 Demo 中的实际用法为准。
CommandBuilder.buildCustomCmd(byte[] data): CommandBase
构建一条需要设备回复的自定义命令,数据载荷为 data。
参数: data (byte[]) —— 业务自定义数据,长度应满足 data.length <= getMaxReceiveMtu(device) - 23。
返回: 携带自定义数据与响应标志的 CommandBase(实际为 CustomCmd)。
CommandBuilder.buildCustomCmdWithoutResponse(byte[] data): CommandBase
构建一条无需设备回复的自定义命令(单向通知)。
参数: data (byte[]) —— 业务自定义数据。
返回: 无响应标志的 CommandBase。发送后回调立即触发且无响应体,业务不应读取 cmd.getResponse()。
WatchManager.sendRcspCommand(BluetoothDevice device, CommandBase cmd, RcspCommandCallback<T> callback)
向目标设备发送 RCSP 命令(含自定义命令)并注册结果回调。
参数:
device(BluetoothDevice) —— 目标设备,通常来自manager.getTargetDevice();cmd(CommandBase) —— 由CommandBuilder构建的命令;callback(RcspCommandCallback<CustomCmd>) —— 结果回调,需实现onCommandResponse(device, cmd)与onErrCode(device, error)。
WatchManager.registerOnRcspCallback(OnRcspCallback callback)
注册全局上行命令回调,设备主动下发的所有 RCSP 命令都会进入 onRcspCommand(device, command)。
注意: 回调是全局的,必须用 command.getId() 过滤出 CMD_CUSTOM / CMD_EXTRA_CUSTOM,再强转为 CustomCmd 处理。
WatchManager.sendCommandResponse(BluetoothDevice device, CustomCmd cmd, Object extra)
回复设备发来的自定义命令。
参数: device 来源设备;cmd 原命令对象(已通过 setStatus() 设置结果、可选 setParam() 设置回复数据);extra 扩展参数,Demo 中固定传 null。
CustomCmd.getStatus() / setStatus(int)、getType()、getResponse()、getParam()、getId()、getOpCodeSn()
getStatus()返回StateCode中的执行状态(STATUS_SUCCESS/STATUS_FAIL/STATUS_UNKOWN_CMD等);getType()返回CommandBase类型标志,用于判断是否需要回复;getResponse()返回CustomResponse(可能为null),getData()取回复字节;getParam()返回CustomParam(可能为null),getData()取参数字节;getId()返回命令 ID,用于回调过滤;getOpCodeSn()返回命令序号,用于错误定位。
DeviceStatusManager.getInstance().getMaxReceiveMtu(BluetoothDevice device): int
查询当前设备协商后的最大接收 MTU,用于计算自定义数据长度上限。
Configuration Options 与约束
自定义命令本身没有配置文件;其"配置"体现在代码常量与运行时参数上:
| 配置项 | 位置/形式 | 默认值 | 说明 |
|---|---|---|---|
| 命令 ID | Command.CMD_CUSTOM / Command.CMD_EXTRA_CUSTOM | SDK 常量 | 接收过滤用,必须与固件侧协商一致;两个 Demo 使用了不同 ID,实际项目统一为一个 |
| 是否需要回复 | buildCustomCmd vs buildCustomCmdWithoutResponse | 需回复 | 单向通知类业务选无回复模式,可减少等待与超时 |
| 数据长度上限 | getMaxReceiveMtu(device) - 23 | 随 MTU | 超过需与固件协商分包;Demo 建议预留余量 |
| 响应数据 | CustomCmd.setParam(...) 后 sendCommandResponse | 空(null) | 需要给设备回数据时设置,Demo 中默认回空 |
| SDK 初始化 | "1.3 配置好 sdk"(Demo 注释) | — | 必须先完成 SDK 配置与连接,WatchManager.getInstance() 才可用 |
Failure Modes, Edge Cases & Concurrency
1. 设备回复异常状态
发送后 cmd.getStatus() != STATUS_SUCCESS 即表示设备执行失败。增强版 Demo 细分了两种场景:
STATUS_UNKOWN_CMD:设备不认识该命令 → 映射为ERR_FUNC_NOT_SUPPORT,并回填opCode与sn,业务可据此提示"固件版本过旧/功能未启用";- 其它非成功状态:用
RcspErrorCode.buildJsonError(...)构造带status详情的错误,便于日志与联调。
设计意图:错误信息携带命令 ID、序号与状态描述,保证在异步回调场景下可精确对账是哪条命令失败。
2. 响应为空(解析失败)
命令类型声明"需要回复",但 cmd.getResponse() 为 null → ERR_PARSE_DATA。可能原因:设备回复帧不完整、数据被截断、或固件对无响应命令误回了空帧。Demo 在读取 response.getData() 之前一律判空,避免 NPE。
3. 接收侧参数为空
设备下发命令但 customCmd.getParam() 为 null(数据区缺失):
- WatchTestTool 版:置
STATUS_FAIL后回执,让设备知道数据非法; - HealthAide 版:仅在该命令需要回复时回执(默认回
SUCCESS空数据),并直接return跳过业务处理。
设计意图:接收侧必须"有来必回"——凡是声明需要回复的命令,即使数据非法也要回执,否则设备会一直等待导致链路阻塞或超时。
4. 命令 ID 过滤错误
onRcspCommand 是所有上行命令的汇聚点,忘记过滤或过滤常量不一致会导致:把普通业务命令强转为 CustomCmd 引发 ClassCastException,或漏掉自定义命令导致业务静默丢失。两个 Demo 使用不同 ID 常量(CMD_CUSTOM vs CMD_EXTRA_CUSTOM)也提示:跨工程移植时必须按实际固件协议统一 ID。
5. 数据超长
自定义数据超过 MTU - 23 时,单包无法承载。Demo 建议与固件协商限长,且"不建议发最大数据"——发送满 MTU 的数据在信号波动时更易丢包,重传成本高。超长数据应走 SDK 的大文件/分包通道。
6. 并发与回调线程
sendRcspCommand与registerOnRcspCallback均为异步回调模型;RcspCommandCallback.onCommandResponse与OnRcspCallback.onRcspCommand可能来自协议栈收发线程,业务侧修改 UI 或共享状态需自行切线程或加锁。- 连续发送多条命令时,
onErrCode中的sn(getOpCodeSn())可用于关联具体命令,SDK 按序号管理应答,业务不应依赖回调顺序。 - 接收回调中的
sendCommandResponse应在当前回调内同步完成,避免跨线程延迟回复导致设备侧超时。
7. 生命周期约束
Demo 注释明确"须在 1.3 配置好 sdk":getInstance() 之前必须完成 SDK 初始化与设备连接;设备断开后发送会立即走 onErrCode。注册的回调建议在页面/服务销毁时反注册,防止内存泄漏与重复回调。
Performance 与 Operational 注意事项
- 净荷预算:每次发送前可用
getMaxReceiveMtu(device)动态计算上限(MTU - 23),并在日志中打印实际发送长度,便于定位"发大包失败"类问题。 - 避免高频单向命令:无回复模式虽省去等待,但大量无回执通知会占用 BLE 带宽,建议业务侧合并/限频。
- 错误码上报:统一使用
BaseError/RcspErrorCode构造错误对象并携带opCode、sn,接入统一日志后能显著降低联调成本。 - 超时兜底:Demo 未显式设置超时,实际产品中建议在
sendRcspCommand外层封装超时与重试策略(SDK 底层对需要回复的命令有应答超时机制,业务需在onErrCode中兜底提示)。
Extension Points
- 自定义命令是"协议级透传"扩展点:客户私有功能只需定义
byte[]载荷协议,无需改动 SDK;CustomParam/CustomResponse的getData()/setData()即数据出入口。 - 接收侧可携带回复数据:HealthAide 版注释展示了
byte[] responseData = new byte[0]; param.setData(responseData); customCmd.setParam(param);的写法——需要给设备返回业务数据时取消注释并填入即可,这是上行命令"请求-应答"能力的扩展点。 - 命令 ID 可协商:
CMD_CUSTOM/CMD_EXTRA_CUSTOM表明 SDK 预留了多个自定义命令槽位;客户可按功能域划分多个 ID(如"传感器配置""显示设置"),在onRcspCommand中分别路由,实现结构化扩展。 - 封装层可复用:
WatchManager(WatchOpImpl子类)将发送/接收/回复统一封装,Demo 的sendCustomCommand/receiveCustomCmd可直接搬入业务 Manager 或 Repository,作为私有协议的基础设施。
Related Links
- CustomCommandDemo.java(WatchTestTool 版) — README 指定的官方参考实现(最简用法)
- CustomCommandDemo.java(HealthAide 版) — 增强版(无回复模式、错误细分、MTU 计算)
- README.md — 功能总览,"自定义命令:支持客户拓展功能",参考 test 包
com.jieli.watchtesttool.CustomCommandDemo - 相关 SDK 包(
com.jieli.jl_rcsp)的CommandBuilder、CustomCmd、OnRcspCallback、DeviceStatusManager等类的源码位于仓库 SDK 目录,建议结合协议文档阅读