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

    • 环境要求与权限配置
    • 集成SDK依赖
    • 运行示例应用
  • 核心架构与协议

    • RCSP协议与数据通道
    • 蓝牙连接与设备管理
    • TWS双耳功能
    • 基础功能接口与自定义命令
  • 设备功能控制

    • 设备音乐控制与ID3信息
    • 文件浏览与传输
    • FM收音与发射
    • 灯光控制
    • 闹钟与时间管理
    • 查找设备与防丢
    • ANC与噪声处理
    • 按键功能设置
    • 彩屏仓控制
    • AI翻译
  • 音效与音频处理

    • 均衡器音效调节
    • 录音与语音控制
    • Line-in、SPDIF与声卡功能
    • 音频编解码库
  • 扩展功能库

    • OTA固件升级
    • 数据加密与解密
    • 图片与动图格式转换
  • 示例应用 btsmart

    • 应用架构与界面导航
    • 设备功能适配与数据层
    • 设备配置JSON与资源文件
  • 参考与版本

    • 错误码参考
    • 版本历史与更新日志
    • 开发文档中心导航

蓝牙连接与设备管理

本文档介绍 PiHome btsmart 应用中蓝牙连接与设备管理能力:包括 JL_BluetoothManager 连接管理器、RCSPController 协议控制器、RCSP 命令/回调体系,以及设备搜索、设备信息查询等核心工作流。所有内容均基于仓库内真实源码(SDK Demo 与测试代码)整理。

Purpose and Scope

本页面覆盖蓝牙连接的端到端机制:从应用层发起操作(搜索设备、查询信息),经过 RCSPController / JL_BluetoothManager 两层 SDK 封装,到 RCSP 命令的构建、发送、响应回调与错误处理,以及连接事件的上报。

本页面不包含的内容(属于同级页面):

  • 具体设备功能(闹钟 Alarm、EQ 音效、FM 收音、助听、灯光控制等)的协议细节——它们复用本文档描述的命令通道,但各自的参数/响应模型属于功能页面。
  • TWS 双耳同步、OTA 固件升级等专项流程。
  • Android 系统蓝牙配对(Bonding)与系统设置层面的内容。

Overview

背景与定位

仓库 code/PiHome_V1.13.0_SDK_V4.2.0/ 下的 btsmart 模块是一款杰理(Jieli)蓝牙音频设备的 Android 控制应用。其蓝牙能力构建在 com.jieli.bluetooth SDK 之上,该 SDK 提供两个核心入口类:

  • JL_BluetoothManager(com.jieli.bluetooth.impl):蓝牙连接管理核心,负责与设备建立/维护连接、发送 RCSP 命令。通过 JL_BluetoothManager.getInstance(context) 获取单例。
  • RCSPController(com.jieli.bluetooth.impl.rcsp):面向业务的高层控制器,封装了"搜索设备"、"请求设备信息"等常用动作,并以 OnRcspActionCallback / BTRcspEventCallback 提供简洁回调。通过 RCSPController.getInstance() 获取单例。

关键概念

概念说明
RCSP 命令应用与设备之间传输的指令,以 CommandBase 为基类,携带参数(Param)与响应(Response)
命令类型标志CommandBase.FLAG_HAVE_PARAMETER_AND_RESPONSE / FLAG_NO_PARAMETER_AND_RESPONSE 等,用于判断命令是否带响应数据
状态码StateCode.STATUS_SUCCESS 表示设备回复成功;其他状态需按错误处理
错误码ErrorCode.SUB_ERR_RESPONSE_BAD_STATUS(设备回复异常状态)、ErrorCode.SUB_ERR_DATA_FORMAT(响应数据格式错误)
正在使用的设备RCSPController.getUsingDevice() 返回当前正在使用的设备;JL_BluetoothManager.getConnectedDevice() 返回已连接设备
设备搜索参数SearchDevParam:包含操作类型 op(Constants.RING_OP_OPEN 开启响铃 / 其他关闭)、超时秒数 timeoutSec、播放方 player(0=App 播放,1=设备播放)

为什么设计为两层 API

从 BtRcspControlDemo.java 可以看出,同一个"获取设备信息"需求提供了两种调用方式:

  • 高层方式:controller.requestDeviceInfo(device, mask, callback),内部自动完成命令构建、发送与解析,业务方只关心成功/失败。
  • 底层方式:manager.sendCommandAsync(device, cmd, timeoutMs, callback),业务方自己用 CommandBuilder 构建命令、检查 cmd.getStatus()、解析响应类型。

这种分层的设计意图是:常规业务走高层 API 减少样板代码;需要精细控制(自定义命令、手动状态校验、超时控制)时走底层 API 获得完整控制权。两种方式共享同一套命令/回调体系,便于在两者之间平滑迁移。

Architecture

下图展示了蓝牙连接与设备管理能力的整体架构:

flowchart TD
    subgraph sg_App["应用层 btsmart"]
        UI["Demo / UI 页面"]
        EventCB["BTRcspEventCallback<br/>事件监听器"]
    end

    subgraph sg_SDK["蓝牙 SDK com.jieli.bluetooth"]
        RCSP["RCSPController<br/>高层动作封装"]
        Mgr["JL_BluetoothManager<br/>连接管理核心"]
        Builder["CommandBuilder<br/>命令构建工具"]
        Cmd["CommandBase<br/>命令模型"]
        RcspCB["RcspCommandCallback<br/>命令回调"]
        ActionCB["OnRcspActionCallback<br/>动作回调"]
        Beans["Bean 模型<br/>DeviceInfo / SearchDevParam"]
        Codes["StateCode / ErrorCode<br/>状态与错误码"]
    end

    subgraph sg_System["系统层"]
        BTStack["Android 蓝牙协议栈"]
    end

    UI -->|"searchDev / requestDeviceInfo"| RCSP
    UI -->|"getInstance(context)"| Mgr
    RCSP -->|"底层命令通道"| Mgr
    Mgr -->|"构建命令"| Builder
    Builder -->|"生成"| Cmd
    Mgr -->|"sendRcspCommand / sendCommandAsync"| Cmd
    Cmd -->|"响应回调"| RcspCB
    Mgr -->|"连接管理"| BTStack
    RCSP -->|"动作结果"| ActionCB
    RCSP -->|"连接事件"| EventCB
    Mgr --> Codes
    RCSP --> Beans

架构说明:

  • 入口层:业务代码(Demo 或 UI)同时持有 RCSPController 与 JL_BluetoothManager 两个单例。前者提供面向场景的封装,后者提供底层连接与命令能力。
  • 命令通道:所有交互最终都归结为"构建 CommandBase → 发送 → 等待设备响应"。CommandBuilder 是命令工厂,RcspCommandCallback 是响应回调契约。
  • 回调双轨:OnRcspActionCallback(一次性动作结果)与 BTRcspEventCallback(持续事件流,如设备主动上报的搜索状态)相互独立,分别对应"请求-应答"与"事件订阅"两种通信模式。
  • 状态/错误码:StateCode 与 ErrorCode 贯穿命令响应解析与异常处理,是错误处理规范化的基础。

核心组件分析

JL_BluetoothManager — 连接管理核心

JL_BluetoothManager 是 SDK 的蓝牙连接枢纽,负责设备连接的建立与维护,并提供命令发送的低层通道。源码中的使用模式(来自 BtRcspControlDemo.java 与 SearchDeviceDemo.java)表明其关键方法如下:

方法作用
JL_BluetoothManager.getInstance(Context)获取全局单例(上下文持有者,应用生命周期内唯一)
getConnectedDevice()返回当前已连接的 BluetoothDevice;发送命令前用于获取目标设备
sendRcspCommand(device, CommandBase, RcspCommandCallback)同步语义的 RCSP 命令发送,响应经 onCommandResponse 回调
sendCommandAsync(device, CommandBase, timeoutMs, RcspCommandCallback)异步命令发送,可显式指定超时时间
getBluetoothOption()返回蓝牙选项对象,getTimeoutMs() 提供默认超时配置

设计意图:单例模式保证连接状态(已连接设备、命令会话)在应用全局唯一,避免多实例导致连接混乱;getConnectedDevice() 与 RCSPController.getUsingDevice() 分别从"连接层"与"使用层"视角暴露当前设备,二者在双设备场景(如 TWS 左右耳)下含义不同。

RCSPController — 高层动作封装

RCSPController 把常用操作封装成"参数 → 动作 → 回调"三段式 API,业务方无需关心命令字节结构。其公开能力(依据 Demo 中的调用点)包括:

方法作用
RCSPController.getInstance()获取全局单例
getUsingDevice()返回当前正在使用的设备
searchDev(device, op, timeoutSec, way, player, callback)发起设备查找(响铃):way 0=全部、1=左耳、2=右耳;player 0=App 播放、1=设备播放
stopSearchDevice(device, callback)停止设备查找
syncSearchDeviceStatus(device, callback)同步查询设备当前的查找状态
requestDeviceInfo(device, mask, callback)请求设备信息,mask 位掩码控制属性范围(0xffffffff 表示全部)
getDeviceInfo()直接读取 SDK 缓存的设备信息(免一次往返)
addBTRcspEventCallback(BTRcspEventCallback)注册全局蓝牙事件监听器

设计意图:searchDev 的 timeoutSec(60 秒)与 player 参数把"查找设备"这一动作的协议细节(响铃开关、超时、播放方)收敛为方法签名;getDeviceInfo() 的缓存读取则是"读写分离"思想的体现——UI 需要反复展示设备信息时不必每次都走蓝牙请求。

命令体系:CommandBase / CommandBuilder / RcspCommandCallback

所有设备交互都基于命令对象,其生命周期在 SearchDeviceDemo.java 中完整呈现:

  1. 构建:CommandBuilder.buildSearchDevStatusCmd() 等工厂方法生成 CommandBase 子类(如 SearchDevCmd、GetTargetInfoCmd)。
  2. 发送:manager.sendRcspCommand(device, cmd, callback) 或 sendCommandAsync(device, cmd, timeoutMs, callback)。
  3. 响应:RcspCommandCallback.onCommandResponse(device, cmd) 收到设备回复;onErrCode(device, BaseError) 收到错误。
  4. 校验:先检查 cmd.getStatus() != StateCode.STATUS_SUCCESS;再根据命令类型标志(FLAG_HAVE_PARAMETER_AND_RESPONSE / FLAG_NO_PARAMETER_AND_RESPONSE)判断是否有响应体,并做空值检查后强转具体响应类型。
  5. 消费:将 CommandBase 强转为具体命令(如 SearchDevCmd),再取其 getResponse() 获取业务数据。

为什么命令要有类型标志:RCSP 协议中命令分为"带参数带响应 / 带参数无响应 / 无参数带响应 / 无参数无响应"四类,CommandBase 的类型标志让回调解析逻辑可以在不感知具体命令的情况下,统一判断"这个命令是否应该携带 Response 数据",从而在响应缺失时立即报 SUB_ERR_DATA_FORMAT 而不是空指针崩溃。

事件订阅:BTRcspEventCallback

与"请求-应答"式回调不同,BTRcspEventCallback 是事件订阅机制。设备在查找过程中会主动上报状态,应用通过 controller.addBTRcspEventCallback(...) 注册监听,在 onSearchDevice(device, SearchDevParam) 中接收事件(参见 SearchDeviceDemo.java)。

事件参数 SearchDevParam 通过 getOp() 区分响铃开启/关闭:op == Constants.RING_OP_OPEN 表示设备开始查找(携带 timeoutSec 超时秒数与 player 播放方),否则表示查找已结束。这种设计让 UI 可以被动感知设备侧状态变化(例如用户在设备上手动停止查找),而无需轮询。

核心流程

流程一:设备查找(搜索响铃)

sequenceDiagram
    participant App as 应用层 Demo
    participant RCSP as RCSPController
    participant Mgr as JL_BluetoothManager
    participant Dev as 蓝牙设备
    participant CB as 回调接口

    App->>RCSP: searchDev(device, RING_OP_OPEN, 60, way, RING_PLAYER_APP, callback)
    RCSP->>Mgr: 构建 SearchDevCmd 并下发
    Mgr->>Dev: 蓝牙通道发送 RCSP 命令
    Dev-->>Mgr: 命令响应
    Mgr-->>CB: onCommandResponse(device, cmd)
    CB-->>App: 校验 STATUS_SUCCESS 后触发 onSuccess(device, Boolean)
    Note over App,CB: 查找期间设备主动上报状态
    Dev-->>RCSP: onSearchDevice(device, SearchDevParam)
    RCSP-->>App: 事件回调(op / timeoutSec / player)
    App->>RCSP: stopSearchDevice(device, callback)
    RCSP->>Dev: 停止查找命令
    Dev-->>RCSP: 响应
    RCSP-->>App: onSuccess / onError

步骤详解(依据 SearchDeviceDemo.java):

  1. 调用 controller.searchDev(...) 传入响铃开关 Constants.RING_OP_OPEN、超时 60 秒、查找侧 way(0=全部,1=左,2=右)与播放方 Constants.RING_PLAYER_APP。
  2. SDK 内部将动作翻译为 SearchDevCmd 并通过 JL_BluetoothManager 下发;动作最终结果经 OnRcspActionCallback.onSuccess / onError 返回。
  3. 若需感知设备侧状态,注册 BTRcspEventCallback,其 onSearchDevice 携带 SearchDevParam(op、timeoutSec、player)。
  4. 查找结束调用 stopSearchDevice 显式停止。

流程二:底层 RCSP 命令收发(以查询查找状态为例)

flowchart TD
    Start([发起操作]) --> Get["manager.getConnectedDevice()"]
    Get --> Build["CommandBuilder.buildSearchDevStatusCmd()"]
    Build --> Send["manager.sendRcspCommand(device, cmd, callback)"]
    Send --> Wait{"等待设备响应"}
    Wait -->|"响应到达"| Resp["onCommandResponse(device, cmd)"]
    Wait -->|"超时/错误"| Err["onErrCode(device, BaseError)"]
    Resp --> Check{"cmd.getStatus() ==<br/>STATUS_SUCCESS?"}
    Check -->|"否"| Bad["构造 BaseError<br/>SUB_ERR_RESPONSE_BAD_STATUS"]
    Check -->|"是"| Cast["强转为 SearchDevCmd"]
    Cast --> NullCheck{"response == null?"}
    NullCheck -->|"是"| DataErr["BaseError<br/>SUB_ERR_DATA_FORMAT"]
    NullCheck -->|"否"| Done([处理 SearchDevStatusResponse])
    Bad --> Done
    DataErr --> Done
    Err --> Done

关键点(依据 SearchDeviceDemo.java):

  • 底层路径要求业务方主动校验两层:先校验设备状态码,再校验响应体空值。这是 SDK 有意为之——底层 API 面向需要精确错误定位的高级调用方。
  • BaseError 统一承载错误码与描述信息,onErrCode 同时收到设备对象,便于按设备维度记录日志。
  • 命令响应的状态检查使用 StateCode.STATUS_SUCCESS;GetTargetInfoCmd 示例(BtRcspControlDemo.java)还展示了用 getType() 判断命令是否携带响应的通用做法。

流程三:设备信息获取(高层 vs 底层)

flowchart LR
    subgraph sg_High["高层 API"]
        A1["requestDeviceInfo(device, 0xffffffff, callback)"] --> A2["onSuccess(device, DeviceInfo)"]
        A1 --> A3["onError(device, BaseError)"]
    end
    subgraph sg_Cache["缓存"]
        B1["getDeviceInfo() 直接返回缓存"]
    end
    subgraph sg_Low["底层 API"]
        C1["buildGetDeviceInfoCmd(mask)"] --> C2["sendCommandAsync(device, cmd, timeoutMs, cb)"]
        C2 --> C3["onCommandResponse + 手动校验状态"]
    end
    UI["业务代码"] --> sg_High
    UI --> sg_Cache
    UI --> sg_Low

三种方式对应不同场景:缓存读取用于频繁展示、允许略旧数据;高层请求用于需要实时刷新且不想处理协议细节;底层命令用于需要自定义 mask、自定义超时或深入调试。这一设计保证 SDK 对业务层"易用性"与"可控性"的平衡。

使用示例

以下示例均从仓库内真实测试代码中提取。

示例一:高层 API 发起设备查找

@Test
public void searchDevice(int way) {
    //获取RCSPController对象
    RCSPController controller = RCSPController.getInstance();
    //way : 0 -- all  1 -- left  2 -- right
    //执行搜索设备功能并等待结果回调
    controller.searchDev(controller.getUsingDevice(), Constants.RING_OP_OPEN, 60, way, Constants.RING_PLAYER_APP, new OnRcspActionCallback<Boolean>() {
        @Override
        public void onSuccess(BluetoothDevice device, Boolean message) {
            //成功回调
        }

        @Override
        public void onError(BluetoothDevice device, BaseError error) {
            //失败回调
            //error - 错误信息
        }
    });
}

Source: SearchDeviceDemo.java

说明:高层 API 将"响铃开/关、超时、查找侧、播放方"收敛为方法参数,业务方只需实现 onSuccess / onError 两个回调即可完成一次设备查找动作。

示例二:注册事件监听接收设备侧状态

@Test
public void searchPhone() {
    //获取RCSPController对象
    RCSPController controller = RCSPController.getInstance();
    //注册蓝牙RCSP事件监听器
    controller.addBTRcspEventCallback(new BTRcspEventCallback() {
        @Override
        public void onSearchDevice(BluetoothDevice device, SearchDevParam searchDevParam) {
            if (searchDevParam.getOp() == Constants.RING_OP_OPEN) { //open ring
                int timeout = searchDevParam.getTimeoutSec();//timeout, unit : second
                int player = searchDevParam.getPlayer(); //player (0 --- app play ring  1 --- device play ring)
            } else {
                //close ring
            }
        }
    });
}

Source: SearchDeviceDemo.java

说明:BTRcspEventCallback.onSearchDevice 属于事件订阅模式,与请求-应答回调相互独立;SearchDevParam 中的 op、timeoutSec、player 三要素完整描述了一次设备查找事件。

示例三:底层 API 构建命令并校验响应

public void syncSearchDeviceStatusV0(Context context) {
    //Step0: 获取JL_BluetoothManager对象
    final JL_BluetoothManager manager = JL_BluetoothManager.getInstance(context);
    //获取已连接和正在使用的设备
    final BluetoothDevice device = manager.getConnectedDevice();
    //Step1: 构建命令 --- 查询查找设备状态
    CommandBase searchDevStatus = CommandBuilder.buildSearchDevStatusCmd();
    //Step2: 执行查询查找设备状态功能并等待结果回调
    manager.sendRcspCommand(device, searchDevStatus, new RcspCommandCallback() {
        @Override
        public void onCommandResponse(BluetoothDevice device, CommandBase cmd) {
            //Step3: 检查设备状态
            if (cmd.getStatus() != StateCode.STATUS_SUCCESS) { //设备状态异常,进行异常处理
                onErrCode(device, new BaseError(ErrorCode.SUB_ERR_RESPONSE_BAD_STATUS, "Device reply an bad status: " + cmd.getStatus()));
                return;
            }
            //成功回调
            SearchDevCmd searchDevCmd = (SearchDevCmd) cmd;
            SearchDevStatusResponse response = (SearchDevStatusResponse) searchDevCmd.getResponse();
            //response -- 查询设备状态
        }

        @Override
        public void onErrCode(BluetoothDevice device, BaseError error) {
            //失败回调
            //error - 错误信息
        }
    });
}

Source: SearchDeviceDemo.java

说明:底层路径遵循"构建 → 发送 → 校验状态 → 强转类型 → 取响应"五步范式,BaseError(ErrorCode, message) 构造方式统一了错误上报格式。

示例四:获取设备信息(高层 + 缓存)

public void getDeviceInfo() {
    //获取RCSPController对象
    RCSPController controller = RCSPController.getInstance();
    //执行请求设备功能并等待结果回调
    controller.requestDeviceInfo(controller.getUsingDevice(), 0xffffffff, new OnRcspActionCallback<DeviceInfo>() {
        @Override
        public void onSuccess(BluetoothDevice device, DeviceInfo message) {
            //成功回调
            //message - 设备信息
        }

        @Override
        public void onError(BluetoothDevice device, BaseError error) {
            //失败回调
            //error - 错误信息
        }
    });
    //第二种方式,获取缓存的设备信息
    DeviceInfo deviceInfo = controller.getDeviceInfo();
}

Source: BtRcspControlDemo.java

说明:mask = 0xffffffff 表示请求全部设备属性;缓存接口 getDeviceInfo() 免去一次蓝牙往返,适合列表页/详情页的即时展示。

示例五:底层 API 自定义超时获取设备信息

public void getDeviceInfoV0(Context context, int mask) {
    //获取JL_BluetoothManager对象
    JL_BluetoothManager manager = JL_BluetoothManager.getInstance(context);
    //构造功能命令
    //mask = 0xffffffff;  -- 获取所有属性
    CommandBase getDeviceInfoCmd = CommandBuilder.buildGetDeviceInfoCmd(mask);
    //执行获取设备信息功能并等待结果回调
    manager.sendCommandAsync(manager.getConnectedDevice(), getDeviceInfoCmd, manager.getBluetoothOption().getTimeoutMs(), new RcspCommandCallback() {
        @Override
        public void onCommandResponse(BluetoothDevice device, CommandBase cmd) {
            //检查设备状态
            if (cmd.getStatus() != StateCode.STATUS_SUCCESS) { //设备状态异常,进行异常处理
                onErrCode(device, new BaseError(ErrorCode.SUB_ERR_RESPONSE_BAD_STATUS, "Device reply an bad status: " + cmd.getStatus()));
                return;
            }
            //成功回调
            //获取对应的命令数据
            GetTargetInfoCmd command = (GetTargetInfoCmd) cmd;
            //获取回复数据
            //注意 - 如果是没有回复数据的命令,回复数据为null
            boolean isHasResponse = command.getType() == CommandBase.FLAG_HAVE_PARAMETER_AND_RESPONSE
                    || command.getType() == CommandBase.FLAG_NO_PARAMETER_AND_RESPONSE;
            if (isHasResponse) {
                TargetInfoResponse response = command.getResponse();
                if (null == response) {
                    onErrCode(device, new BaseError(ErrorCode.SUB_ERR_DATA_FORMAT, "Response data is error."));
                    return;
                }
                //处理设备回复数据
            }
        }

        @Override
        public void onErrCode(BluetoothDevice device, BaseError error) {
            //失败回调
            //error - 错误信息
        }
    });
}

Source: BtRcspControlDemo.java

说明:此示例展示了完整的防御式解析——先用 getType() 判断命令是否应带响应,再对 getResponse() 做空值检查,最后才使用数据。manager.getBluetoothOption().getTimeoutMs() 提供默认超时,也可自行传入自定义超时值。

配置选项

配置项类型默认值(依据源码调用)说明
BluetoothOption.getTimeoutMs()long由 JL_BluetoothManager.getBluetoothOption() 提供底层命令发送默认超时,可被 sendCommandAsync 显式参数覆盖
searchDev 的 timeoutSecint60(Demo 示例值)设备查找响铃的持续秒数
searchDev 的 wayint0=全部 / 1=左 / 2=右指定查找哪一侧耳机(TWS 场景)
searchDev 的 playerintConstants.RING_PLAYER_APP(0)响铃播放方:0=App 播放,1=设备播放
requestDeviceInfo 的 maskint0xffffffff(全部属性)位掩码,控制请求哪些设备属性
RING_OP_OPENintConstants.RING_OP_OPEN响铃开启标志,事件回调中用于区分开关

API 参考

JL_BluetoothManager

方法签名说明
static JL_BluetoothManager getInstance(Context context)获取全局单例;首次调用初始化连接管理资源
BluetoothDevice getConnectedDevice()返回当前已连接的蓝牙设备,未连接时可能为 null
void sendRcspCommand(BluetoothDevice device, CommandBase cmd, RcspCommandCallback callback)发送 RCSP 命令,等待设备响应回调
void sendCommandAsync(BluetoothDevice device, CommandBase cmd, long timeoutMs, RcspCommandCallback callback)异步发送命令并指定超时时间
BluetoothOption getBluetoothOption()获取蓝牙选项配置(含默认超时 getTimeoutMs())

RCSPController

方法签名说明
static RCSPController getInstance()获取全局单例
BluetoothDevice getUsingDevice()返回当前正在使用的设备(可能不同于已连接设备)
void searchDev(BluetoothDevice device, int op, int timeoutSec, int way, int player, OnRcspActionCallback<Boolean> callback)查找设备;op 响铃开关、way 0/1/2、player 0=App/1=设备
void stopSearchDevice(BluetoothDevice device, OnRcspActionCallback<Boolean> callback)停止设备查找
void syncSearchDeviceStatus(BluetoothDevice device, OnRcspActionCallback<SearchDevStatusResponse> callback)同步查询查找设备状态
void requestDeviceInfo(BluetoothDevice device, int mask, OnRcspActionCallback<DeviceInfo> callback)请求设备信息(mask 位掩码)
DeviceInfo getDeviceInfo()读取缓存设备信息
void addBTRcspEventCallback(BTRcspEventCallback callback)注册蓝牙事件监听(含 onSearchDevice)

回调接口

RcspCommandCallback(底层命令回调)

  • onCommandResponse(BluetoothDevice device, CommandBase cmd):设备响应到达;调用方需自行校验 cmd.getStatus() == StateCode.STATUS_SUCCESS 并解析响应体。
  • onErrCode(BluetoothDevice device, BaseError error):命令发送失败、超时或设备状态异常。

OnRcspActionCallback<T>(高层动作回调)

  • onSuccess(BluetoothDevice device, T message):动作成功,message 为结果对象(如 Boolean、DeviceInfo、SearchDevStatusResponse)。
  • onError(BluetoothDevice device, BaseError error):动作失败,error 携带错误码与描述。

BTRcspEventCallback(事件订阅)

  • onSearchDevice(BluetoothDevice device, SearchDevParam searchDevParam):设备侧查找状态变化事件;searchDevParam.getOp() 区分响铃开关,getTimeoutSec() 为超时秒数,getPlayer() 为播放方。

数据模型

  • CommandBase:命令基类。getStatus() 返回设备状态码;getType() 返回命令类型标志(FLAG_HAVE_PARAMETER_AND_RESPONSE / FLAG_NO_PARAMETER_AND_RESPONSE);子类通过 getResponse() 暴露响应体。
  • SearchDevCmd / SearchDevParam / SearchDevStatusResponse:查找设备命令三元组(命令 / 参数 / 响应)。
  • GetTargetInfoCmd / TargetInfoResponse:获取设备信息命令与响应。
  • DeviceInfo:设备信息聚合模型,由 requestDeviceInfo 返回并缓存于 RCSPController。
  • BaseError:统一错误模型,由 ErrorCode 枚举 + 描述字符串构造,如 new BaseError(ErrorCode.SUB_ERR_RESPONSE_BAD_STATUS, "...")。

失败模式、边界情况与并发

设备状态码异常

所有命令响应的首要检查点是 cmd.getStatus() != StateCode.STATUS_SUCCESS。设备可能返回"忙碌、不支持、参数错误"等状态,统一转换为 SUB_ERR_RESPONSE_BAD_STATUS 错误(携带实际状态码以便排查)。设计意图:把设备侧的状态错误与传输层错误归一到同一个 onErrCode 通道,简化调用方的错误分支。

响应体为空

即使状态码为成功,命令也可能没有响应数据(FLAG_NO_PARAMETER_AND_RESPONSE 类命令),或响应解析失败返回 null。底层解析模式先用 getType() 判断是否应携带响应,再对 getResponse() 做 null 检查,命中则报 SUB_ERR_DATA_FORMAT。设计意图:防御式解析避免强转空对象导致 NullPointerException,并将数据损坏问题显式化。

设备未连接 / 设备为 null

getConnectedDevice() 与 getUsingDevice() 在未连接状态下可能返回 null;Demo 中所有发送前都先取设备再构建命令。实际业务应在连接事件回调(BTRcspEventCallback 体系)中确认连接就绪后再发起操作,避免向 null 设备发送命令。

超时控制

底层 API 通过 sendCommandAsync(device, cmd, timeoutMs, callback) 显式控制等待时长,默认值来自 getBluetoothOption().getTimeoutMs()。超时后 SDK 触发 onErrCode,调用方不应继续等待 onCommandResponse——两路回调互斥返回。

事件与请求的并发

BTRcspEventCallback(事件流)与 RcspCommandCallback(请求-应答)可能同时触发:例如设备查找期间,searchDev 的 onSuccess 与 onSearchDevice 事件会交错到达。业务代码应确保 UI 状态更新具备幂等性,避免"响铃已停止但事件仍上报"造成状态回退。

双设备(TWS)场景

way 参数(0=全部 / 1=左 / 2=右)表明同一命令可定向到单侧耳机;getUsingDevice() 与 getConnectedDevice() 的差异在双设备连接时尤为关键——"正在使用"的设备才是大部分业务动作的目标。

性能与运维

  • 缓存优先:controller.getDeviceInfo() 缓存读取避免高频 UI 刷新触发蓝牙往返;仅在需要实时数据时调用 requestDeviceInfo。
  • 避免重复注册:addBTRcspEventCallback 为全局事件监听,建议在组件生命周期内注册一次(onCreate/onResume),并在销毁时反注册,防止内存泄漏与重复回调。
  • 命令串行化:蓝牙 SPP/LE 通道带宽有限,高频命令(如 EQ 拖动、音量调节)建议节流;sendRcspCommand 与 sendCommandAsync 的选择需结合业务对超时的敏感性。
  • 日志:SDK 提供 JL_Log 工具(Demo 中导入),排查连接问题时关注命令构建、发送与回调三处的日志时间戳。

扩展点

  1. 新命令接入:实现 CommandBase 子类 + 对应 Param/Response 模型,在 CommandBuilder 增加工厂方法,即可复用 sendRcspCommand 通道,无需改动连接层。
  2. 新事件订阅:扩展 BTRcspEventCallback 增加回调方法,SDK 在对应事件点触发,业务方通过既有 addBTRcspEventCallback 注册。
  3. 自定义超时策略:底层 API 的 timeoutMs 参数支持按命令差异化配置,可依据设备类型或命令复杂度动态计算。
  4. 高层动作封装:仿照 searchDev / requestDeviceInfo 模式,将新业务动作封装进 RCSPController,统一走 OnRcspActionCallback 回调契约。

测试覆盖

仓库中与蓝牙连接相关的测试集中在 btsmart/src/test/java/com/jieli/btsmart/demo/ 下:

  • SearchDeviceDemo.java:覆盖设备查找(高层 searchDev / stopSearchDevice / syncSearchDeviceStatus)与底层命令路径(buildSearchDevStatusCmd + sendRcspCommand)。
  • BtRcspControlDemo.java:覆盖设备信息获取(高层 requestDeviceInfo、缓存 getDeviceInfo、底层 sendCommandAsync),以及系统信息、设备模式切换、重启等 RCSP 控制命令。
  • TwsDemo.java:TWS 信息获取,展示 getUsingDevice() 在双设备场景下的用法。

这些测试以"Demo 即用法文档"的方式,明确了每个 API 的调用顺序、回调语义与错误处理范式,是接入 SDK 的最佳参考。

Related Links

  • SearchDeviceDemo.java(设备查找 Demo)
  • BtRcspControlDemo.java(RCSP 控制 Demo)
  • TwsDemo.java(TWS 双耳 Demo)
  • 设备功能页面:闹钟(Alarm)、EQ 音效、FM 收音、助听(Hearing)、灯光控制等均复用本文档的命令通道,其参数模型见同级功能页面。
  • TWS 双耳同步与 OTA 固件升级为独立专项流程,详见对应页面。
Prev
RCSP协议与数据通道
Next
TWS双耳功能