蓝牙连接库
HealthAide 应用中的蓝牙连接能力由两层构成:底层是杰理科技封装的 com.jieli.bluetooth_connect SDK(外部依赖,负责 BLE/SPP 扫描、连接、GATT 数据收发与历史记录),上层是本仓库内 com.jieli.healthaide.tool.bluetooth 包下的应用层封装(BluetoothHelper 门面类、BluetoothEventListener 事件抽象与 BtEventCbManager 事件分发),两者配合 RCSP 协议层实现手表设备的完整连接生命周期。
Purpose and Scope
本页聚焦"蓝牙连接库"这一能力边界,覆盖:
- 应用层对
bluetooth_connectSDK 的初始化与配置(BluetoothOption、BluetoothCore.init); BluetoothHelper单例门面提供的连接、断开、MTU 修改、历史记录、设备认证等对外 API;- 连接方式的决策逻辑(BLE / SPP / BLE→SPP 切换、OTA 升级设备特殊路径);
- 事件回调体系(
BluetoothEventListener→BtEventCbManager的注册与分发); - 与 RCSP 协议层(
RcspAuth、RcspOpImpl)的协作方式。
以下内容不属于本页范围,请参见对应页面:
- 手表业务指令的封装与 RCSP 协议交互,参见 WatchManager(手表管理);
- 应用整体启动流程与配置加载,参见 HealthApplication / ConfigHelper;
- 升级(OTA)流程细节,参见设备升级相关页面。
注意:
com.jieli.bluetooth_connect库本身以 AAR 形式引入,其源码不在本仓库内(仓库中ListFiles未发现bluetooth_connect/**源码目录)。本页以仓库内可验证的应用层封装代码与测试代码为依据进行说明;SDK 内部实现细节标注为"外部依赖,未在仓库源码中"。
Overview
HealthAide 通过蓝牙与杰理系智能手表通信。连接库解决的核心问题是:如何稳定、可重用地管理一条或多条蓝牙链路,并把底层 Android BLE/GATT 细节与上层业务解耦。
设计上采用经典的分层 + 门面(Facade)+ 单例模式:
- 门面:
BluetoothHelper是应用全局唯一的蓝牙操作入口,采用双重检查锁(DCL)单例,内部持有BluetoothManager(SDK 连接管理器)与RcspAuth(RCSP 设备认证器); - 事件总线:
BluetoothHelper内部注册一个 SDK 回调mBtEventCallback,再通过BtEventCbManager把事件扇出给任意数量的BluetoothEventListener业务监听者(页面/管理器),实现"一个生产者、多个消费者"的订阅模型; - 策略化连接:同一设备可能走 BLE、SPP 或"先 BLE 后切换 SPP"三种路径,连接库根据广播信息(
BleScanMessage)、历史记录(HistoryRecord)与设备类型(JL_DeviceType)做运行时决策; - 状态管理:连接状态、认证状态(
mAuthDeviceMap)、待更新设备(needUpdateDeviceMap)、BLE→SPP 切换参数(connectSppParam)均以内存 Map/对象维护,并通过 Handler 主线程消息做超时兜底。
flowchart TD
subgraph sg_App["应用层 (com.jieli.healthaide)"]
UI["业务页面 / WatchManager"]
Helper["BluetoothHelper<br/>(单例门面)"]
CbMgr["BtEventCbManager<br/>(事件分发)"]
Listener["BluetoothEventListener<br/>(抽象监听器)"]
UI --> Helper
Helper --> CbMgr
CbMgr --> Listener
UI -.implements.-> Listener
end
subgraph sg_Sdk["连接库层 (com.jieli.bluetooth_connect 外部 AAR)"]
Core["BluetoothCore<br/>(全局初始化)"]
Manager["BluetoothManager<br/>(连接管理器)"]
Option["BluetoothOption<br/>(连接配置)"]
Auth["RcspAuth<br/>(设备认证)"]
end
subgraph sg_Proto["协议层 (com.jieli.jl_rcsp)"]
RcspOp["RcspOpImpl<br/>(RCSP 指令操作)"]
end
subgraph sg_Android["Android 系统层"]
Adapter["BluetoothAdapter / GATT"]
end
Helper --> Core
Helper --> Manager
Helper --> Auth
Helper --> RcspOp
Manager --> Option
Manager --> Adapter
Auth --> Manager
RcspOp --> Helper
架构说明:
BluetoothHelper构造时若BluetoothCore.isInit()为 false 则用BluetoothOption.createDefaultOption()定制参数完成 SDK 全局初始化,随后获取BluetoothManager单例并注册回调,创建RcspAuth,最后调用fastConnect()尝试回连历史设备(见 BluetoothHelper.java);- 所有 SDK 回调在
mBtEventCallback(BluetoothEventCallback实现)中统一收敛,经BtEventCbManager分发给注册的BluetoothEventListener; RcspAuth通过this::sendDataToDevice方法引用把认证指令经BluetoothManager下发,认证结果回写mAuthDeviceMap,用于isConnectedDevice()的"已连接且已认证"判定。
核心实现剖析
1. SDK 初始化与连接配置
BluetoothHelper 的私有构造函数是连接库的装配点。它把 SDK 的启动参数收敛到 BluetoothOption 中,避免业务方各自初始化:
private BluetoothHelper() {
BluetoothOption bluetoothOption = BluetoothOption.createDefaultOption()
.setPriority(HealthConstant.DEFAULT_CONNECT_WAY)
.setScanFilterData("")
.setNeedChangeBleMtu(false)
.setMtu(BluetoothConstant.BLE_MTU_MAX)
.setUseMultiDevice(false);
mContext = HealthApplication.getAppViewModel().getApplication();
if (!BluetoothCore.isInit()) {
BluetoothCore.init(mContext, bluetoothOption);
}
mBluetoothOp = BluetoothManager.getInstance();
mBluetoothOp.registerBluetoothCallback(mBtEventCallback);
mRcspAuth = new RcspAuth(this::sendDataToDevice, mRcspAuthListener);
mBtEventCbManager = new BtEventCbManager();
mBluetoothOp.fastConnect();
}
Source: BluetoothHelper.java
设计意图:
setPriority(HealthConstant.DEFAULT_CONNECT_WAY)指定扫描/连接优先采用的协议类型(BLE 或 SPP),由全局配置常量控制;setNeedChangeBleMtu(false)+setMtu(BLE_MTU_MAX)表明 SDK 层面不自动改 MTU,改由应用层在连接成功后按需调用requestBleMtu(见下文"MTU 修改"),这是把异步时序风险留给应用层可控管理的取舍;setUseMultiDevice(false)声明单设备连接模式,简化了连接状态机;- 构造末尾立即
fastConnect(),让 App 启动即自动回连上次使用过的设备,提升用户"拿起即用"的体验。
单例采用双重检查锁,保证多线程首次访问只构造一次;destroy() 会反注册回调、销毁 BluetoothManager 与 RcspAuth、清理内存 Map 与 Handler 消息,并把 instance 置空以便重建。
2. 连接入口与协议决策
connectDevice(BluetoothDevice) 是基础连接入口。它根据设备类型与历史记录决定走 BLE 还是 SPP:
public boolean connectDevice(BluetoothDevice device) {
if (null == device || !ConnectUtil.isHasConnectPermission(mContext)) return false;
int devType = device.getType();
int connectWay = BluetoothConstant.PROTOCOL_TYPE_BLE;
if (!HealthConstant.ONLY_CONNECT_BLE) {
if (devType == BluetoothDevice.DEVICE_TYPE_UNKNOWN || devType == BluetoothDevice.DEVICE_TYPE_DUAL) {
connectWay = getCacheConnectWay(device);
if (connectWay == BluetoothConstant.PROTOCOL_TYPE_SPP) {
String mappedAddress = mBluetoothOp.getMappedDeviceAddress(device.getAddress());
if (BluetoothAdapter.checkBluetoothAddress(mappedAddress)) {
BluetoothDevice mappedDev = getRemoteDevice(mappedAddress);
if (mappedDev != null && mappedDev.getType() != BluetoothDevice.DEVICE_TYPE_LE) {
device = mappedDev;
}
}
}
}
}
return mBluetoothOp.connectBtDevice(device, connectWay);
}
Source: BluetoothHelper.java
逻辑要点:
- 入口先做权限校验(
ConnectUtil.isHasConnectPermission),这是 Android 6.0+ 动态权限的必要防线; HealthConstant.ONLY_CONNECT_BLE为 true 时强制 BLE,屏蔽 SPP 路径;- 对
DEVICE_TYPE_UNKNOWN(广播包未携带类型)与DEVICE_TYPE_DUAL(双模)设备,调用getCacheConnectWay依据历史记录取回上次连接方式; - SPP 路径上会把 BLE 地址映射为经典蓝牙(EDR)地址(
getMappedDeviceAddress),并校验映射地址合法且非纯 LE 设备后才替换目标设备。
带广播信息的重载 connectDevice(BluetoothDevice, BleScanMessage) 则利用扫描包做出更精确的决策:
- 广播包
connectWay == SPP时,若为手表设备(JL_DEVICE_TYPE_WATCH)且固件版本 v1 或 version Bit(1) 置位,则先走 BLE 再切 SPP(兼容 BR28 手表),否则直接用广播中的edrAddr连接经典蓝牙; - 若
scanMessage.isOTA()(升级固件的设备),把 OTA 广播地址记录进历史记录(historyRecord.setUpdateAddress),或对新设备放入needUpdateDeviceMap待连接后处理升级(见 BluetoothHelper.java)。
3. BLE→SPP 切换(bleChangeSpp)
手表类设备为提高数据吞吐会选择从 BLE 切换到 SPP。切换动作由 RCSP 指令驱动:
public void bleChangeSpp(@NonNull RcspOpImpl rcspOp, @NonNull BluetoothDevice device) {
DeviceInfo deviceInfo = rcspOp.getDeviceInfo(device);
connectSppParam = new ConnectSppParam(device.getAddress(), deviceInfo.getEdrAddr());
rcspOp.sendRcspCommand(device, CommandBuilder.buildNotifyCommunicationWayCmd(BluetoothConstant.PROTOCOL_TYPE_SPP, 0),
new BooleanRcspActionCallback("bleChangeSpp", new OnOperationCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
JL_Log.i(TAG, "bleChangeSpp", "Waiting for ble disconnect...");
mHandler.removeMessages(MSG_CHECK_BLE_DISCONNECT);
mHandler.sendEmptyMessageDelayed(MSG_CHECK_BLE_DISCONNECT, CHECK_DELAY);
}
@Override
public void onFailed(BaseError error) {
JL_Log.w(TAG, "bleChangeSpp", "onErrCode ---> " + error);
if (mHandler.hasMessages(MSG_CHECK_BLE_DISCONNECT)) {
mHandler.removeMessages(MSG_CHECK_BLE_DISCONNECT);
mHandler.sendEmptyMessage(MSG_CHECK_BLE_DISCONNECT);
}
}
}));
}
Source: BluetoothHelper.java
切换成功后固件会主动断开 BLE,应用侧用 MSG_CHECK_BLE_DISCONNECT 延迟消息兜底:若 3 秒后 BLE 仍连接着,主线程 Handler 会强制 disconnectDevice(bleDev)(见 BluetoothHelper.java)。指令失败时则立即触发同一兜底逻辑。connectSppParam 记录 BLE 地址与 EDR 地址,供 isBleChangeSpp(device) 判断当前是否处于切换期。
4. MTU 修改与超时保护
BLE 数据吞吐依赖 MTU 协商。startChangeMtu 用任务对象防重入,并设置 5 秒超时:
private boolean startChangeMtu(BluetoothDevice device, int changeMtu) {
if (mChangeBleMtuTimeoutTask != null) {
JL_Log.w(TAG, "startChangeMtu", "Adjusting the MTU for BLE");
return true;
}
boolean ret = mBluetoothOp.requestBleMtu(device, changeMtu);
JL_Log.i(TAG, "startChangeMtu", "requestBleMtu = " + ret + ", change mtu = " + changeMtu);
if (ret) {
mChangeBleMtuTimeoutTask = new ChangeBleMtuTimeoutTask(device);
mHandler.postDelayed(mChangeBleMtuTimeoutTask, DELAY_WAITING_TIME);
}
return ret;
}
Source: BluetoothHelper.java
要点:请求已发出(requestBleMtu 返回 true)后启动 5 秒超时任务,MTU 回调到达时调用 stopChangeBleMtu() 移除任务;超时未回调则按失败处理。这样既避免重复请求,又防止 GATT 回调丢失导致永久等待。
5. 设备认证与连接语义
连接库把"蓝牙已连接"与"业务可用"区分开:isConnectedDevice() 要求设备已连接且通过 RCSP 认证:
public boolean isConnectedDevice() {
return getConnectedBtDevice() != null && isDevAuth(getConnectedBtDevice().getAddress());
}
private boolean isDevAuth(String address) {
if (!isEnableDeviceAuth()) return true;
Boolean b = mAuthDeviceMap.get(address);
return b != null && b;
}
Source: BluetoothHelper.java 与 BluetoothHelper.java
- 认证开关来自
ConfigHelper.getInstance().isEnableDeviceAuth()(可远程/本地配置),关闭时isDevAuth恒为 true; - 开启时,认证结果由
RcspAuth的回调写入mAuthDeviceMap(setDevAuth),断开时removeDevAuth清除; RcspAuth的认证指令下发复用sendDataToDevice(device, data)→mBluetoothOp.sendDataToDevice,即认证报文与业务报文走同一条链路,保证了时序一致性。
6. 事件回调体系
BluetoothEventListener 是应用侧统一的事件抽象(抽象类,全部方法空实现),SDK 事件经 BtEventCbManager 扇出。回调覆盖:
| 回调方法 | 触发时机 |
|---|---|
onAdapterStatus(boolean bEnabled) | 蓝牙适配器开关状态变化 |
onBtDiscoveryStatus(boolean bBle, boolean bStart) | 扫描开始/结束 |
onBtDiscovery(BluetoothDevice device, BleScanMessage bleScanMessage) | 扫描到设备(携带广播信息) |
onShowDialog(BluetoothDevice device, BleScanMessage bleScanMessage) | 产品弹窗(发现可连接产品) |
onBleMtuChange(BluetoothGatt gatt, int mtu, int status) | BLE MTU 协商结果 |
onConnection(BluetoothDevice device, int status) | 连接状态变化(见 BluetoothConstant.CONNECT_STATE_*) |
onReceiveData(BluetoothDevice device, byte[] data) | 收到裸数据(RCSP 帧解析前) |
onSwitchConnectedDevice(BluetoothDevice device) | 使用中的设备切换 |
onHistoryRecord(int op, HistoryRecord record) | 历史记录增删(0 添加 / 1 删除) |
onError(ErrorInfo error) | 连接/数据错误 |
Source: BluetoothEventListener.java
BluetoothHelper.addBluetoothEventListener/removeBluetoothEventListener 委托给 BtEventCbManager,页面在 onResume/onDestroy 中注册/注销即可,多个页面可同时监听互不干扰。destroy() 中会清空所有监听器,防止 Activity 泄漏。
核心流程
下图描述从扫描到设备可用的完整连接生命周期(以手表 BLE 连接为例):
sequenceDiagram
participant UI as 业务页面
participant H as BluetoothHelper
participant M as BluetoothManager(SDK)
participant A as Android BLE 栈
participant CB as mBtEventCallback
participant B as BtEventCbManager
participant L as BluetoothEventListener
UI->>H: connectDevice(device, scanMessage)
H->>H: 解析连接方式(BLE/SPP/OTA)
H->>M: connectBtDevice(device, connectWay)
M->>A: BluetoothGatt connect
A-->>CB: onConnection(device, CONNECTING)
CB->>B: 分发
B->>L: onConnection(device, CONNECTING)
A-->>CB: onConnection(device, CONNECTED)
CB->>B: 分发
B->>L: onConnection(device, CONNECTED)
H->>M: requestBleMtu(device, BLE_MTU_MAX)
A-->>CB: onBleMtuChange(gatt, mtu, status)
CB->>B: 分发
B->>L: onBleMtuChange(...)
H->>RcspAuth: 认证指令(sendDataToDevice)
M-->>CB: onReceiveData(device, data)
CB->>H: mRcspAuthListener 认证成功
H->>H: setDevAuth(device, true)
H-->>UI: isConnectedDevice() == true
流程解读:
- 业务方调用
connectDevice,BluetoothHelper完成协议决策(BLE / SPP / OTA 特殊路径)后转交BluetoothManager; - SDK 内部驱动 Android
BluetoothGatt建立链路,连接状态通过onConnection回调上行,经BtEventCbManager广播给所有监听者; - 连接成功后应用层主动
requestBleMtu提升吞吐,MTU 结果同样走事件链路上报; - RCSP 认证报文通过
sendDataToDevice下发,认证结果写入mAuthDeviceMap,此后isConnectedDevice()才返回 true——业务层用这一语义决定是否展示"已连接" UI 或允许数据操作; - 数据接收(
onReceiveData)始终先经mBtEventCallback汇聚,属于 RCSP 指令的数据再由上层协议栈(WatchManager / RcspOpImpl)解析。
使用示例
示例 1:应用启动时配置 SDK 日志
在 HealthApplication.onCreate 中把日志开关同步给连接库与 RCSP 层:
JL_Log.configureLog(this, isEnableLogFunc, isEnableLogFunc);
com.jieli.bluetooth_connect.util.JL_Log.setLog(isEnableLogFunc);
com.jieli.bluetooth_connect.util.JL_Log.setLogOutput(JL_Log::addLogOutput);
Source: HealthApplication.java
说明:JL_Log::addLogOutput 是应用自定义的日志输出方法引用,用于把 SDK 日志重定向到统一日志系统,便于线上问题排查。
示例 2:获取单例并监听连接事件
测试代码展示了最典型的使用姿势——注册监听器、获取连接状态、发起连接:
import com.jieli.bluetooth_connect.constant.BluetoothConstant;
import com.jieli.bluetooth_connect.util.ConnectUtil;
import com.jieli.healthaide.tool.bluetooth.BluetoothEventListener;
import com.jieli.healthaide.tool.bluetooth.BluetoothHelper;
BluetoothEventListener listener = new BluetoothEventListener() {
@Override
public void onRcspInit(BluetoothDevice device, boolean isInit) {
super.onRcspInit(device, isInit); // RCSP 协议初始化状态
}
@Override
public void onMandatoryUpgrade(BluetoothDevice device) {
super.onMandatoryUpgrade(device); // 强升设备回调
}
@Override
public void onWatchSystemException(BluetoothDevice device, int sysStatus) {
super.onWatchSystemException(device, sysStatus); // 0 为正常,其他为错误码
}
};
BluetoothHelper.getInstance().addBluetoothEventListener(listener);
Source: WatchManagerTest.java
说明:WatchManagerTest 通过继承 BluetoothEventListener 并覆写 RCSP 相关回调来观测连接/协议状态,这反映了连接库与 RCSP 协议层的协作契约——连接库只负责链路,协议事件由上层监听器继续处理。
示例 3:连接、状态查询与断开
public boolean connectDevice(BluetoothDevice device) {
if (null == device || !ConnectUtil.isHasConnectPermission(mContext)) return false;
// ... 协议决策 ...
return mBluetoothOp.connectBtDevice(device, connectWay);
}
public int getConnectionStatus(BluetoothDevice device) {
if (null == device) return BluetoothConstant.CONNECT_STATE_DISCONNECT;
int status = BluetoothConstant.CONNECT_STATE_DISCONNECT;
if (isConnectedBtDevice(device)) {
status = BluetoothConstant.CONNECT_STATE_CONNECTED;
} else if (BluetoothUtil.deviceEquals(device, getBluetoothOp().getConnectingDevice())) {
status = BluetoothConstant.CONNECT_STATE_CONNECTING;
}
return status;
}
public void disconnectDevice(BluetoothDevice device) {
if (null == device || !ConnectUtil.isHasConnectPermission(mContext)) return;
if (isConnectedBtDevice(device)) {
mBluetoothOp.disconnectBtDevice(device);
} else {
publishDeviceConnectionStatus(device, BluetoothConstant.CONNECT_STATE_DISCONNECT);
}
}
Source: BluetoothHelper.java、BluetoothHelper.java、BluetoothHelper.java
说明:getConnectionStatus 归一化三种状态(断开/连接中/已连接),UI 层可直接绑定;disconnectDevice 对未连接设备会主动补发一条 CONNECT_STATE_DISCONNECT 事件,保证监听者状态一致。
配置选项
连接库的配置集中在 BluetoothOption(SDK 初始化参数)与 HealthConstant(应用全局常量)两处。
BluetoothOption(SDK 初始化)
| 配置项 | 类型 | 默认值(本仓库实际设置) | 说明 |
|---|---|---|---|
setPriority(connectWay) | int | HealthConstant.DEFAULT_CONNECT_WAY | 扫描/连接优先协议(BLE 或 SPP) |
setScanFilterData(filter) | String | "" | 扫描过滤数据(空为不过滤) |
setNeedChangeBleMtu(need) | boolean | false | SDK 是否自动协商 MTU(应用层自行控制) |
setMtu(mtu) | int | BluetoothConstant.BLE_MTU_MAX | 目标 MTU 值 |
setUseMultiDevice(use) | boolean | false | 是否支持多设备同时连接 |
setSkipNoneNameDevice(skip) | boolean | 注释掉(未启用) | Android 13 上跳过无名称设备(注释保留备选) |
Source: BluetoothHelper.java
应用级开关(HealthConstant / ConfigHelper)
| 开关 | 类型 | 说明 |
|---|---|---|
HealthConstant.ONLY_CONNECT_BLE | boolean | true 时强制所有连接走 BLE,屏蔽 SPP 与 EDR 映射 |
HealthConstant.DEFAULT_CONNECT_WAY | int | 默认连接协议(注入 BluetoothOption.setPriority) |
ConfigHelper.isEnableDeviceAuth() | boolean | 是否启用 RCSP 设备认证流程(关闭则 isDevAuth 恒 true) |
DELAY_WAITING_TIME = 5000L | long | MTU 修改超时(毫秒) |
CHECK_DELAY = 3000 | int | BLE→SPP 切换后 BLE 断开兜底检查延迟(毫秒) |
Source: BluetoothHelper.java
API 参考
BluetoothHelper(单例门面)
| 方法签名 | 返回值 | 说明 |
|---|---|---|
static BluetoothHelper getInstance() | BluetoothHelper | DCL 单例获取;首次调用触发 SDK 初始化与 fastConnect() |
void addBluetoothEventListener(BluetoothEventListener listener) | void | 注册事件监听(委托 BtEventCbManager) |
void removeBluetoothEventListener(BluetoothEventListener listener) | void | 注销事件监听 |
void destroy() | void | 反注册回调、销毁 SDK 与认证器、清理状态 |
BluetoothManager getBluetoothOp() | BluetoothManager | 暴露 SDK 连接管理器(高级用法) |
boolean isConnectedBtDevice(BluetoothDevice) | boolean | 设备是否已连接(链路层) |
boolean isUsedBtDevice(BluetoothDevice) | boolean | 设备是否连接且为当前使用设备 |
boolean isHistoryRecord(String devAddress) | boolean | 是否存在历史记录 |
boolean isConnectedDevice() | boolean | 是否已连接且已认证(业务可用语义) |
BluetoothDevice getConnectedBtDevice() | BluetoothDevice | 当前连接设备 |
BluetoothGatt getConnectedBluetoothGatt(BluetoothDevice) | BluetoothGatt | 获取 GATT 控制对象 |
int getConnectionStatus(BluetoothDevice) | int | CONNECT_STATE_DISCONNECT/CONNECTING/CONNECTED |
boolean connectDeviceWithoutRecord(BluetoothDevice) | boolean | 连接且不写入历史记录 |
boolean connectDevice(BluetoothDevice) | boolean | 基础连接(自动决策 BLE/SPP) |
boolean connectDevice(BluetoothDevice, BleScanMessage) | boolean | 基于广播信息连接(支持 OTA 设备) |
void connectHistoryRecord(HistoryRecord, OnHistoryRecordCallback) | void | 按历史记录回连 |
void removeHistoryRecord(String, OnHistoryRecordCallback) | void | 删除历史记录 |
int getCacheConnectWay(BluetoothDevice) | int | 从历史记录推断上次连接方式 |
boolean sendDataToDevice(BluetoothDevice, byte[]) | boolean | 发送裸数据到设备 |
void disconnectDevice(BluetoothDevice) | void | 断开连接(未连接时补发断开事件) |
boolean isAuthDevice(BluetoothDevice) | boolean | 设备是否已通过认证 |
void syncEdrConnectionStatus(BluetoothDevice, DeviceInfo) | void | 同步经典蓝牙连接状态(抢连处理) |
void bleChangeSpp(RcspOpImpl, BluetoothDevice) | void | 发起 BLE→SPP 切换(RCSP 指令) |
boolean isBleChangeSpp(BluetoothDevice) | boolean | 是否处于 BLE→SPP 切换期 |
BluetoothEventListener(抽象监听器)
全部方法为空实现的钩子,业务方可按需覆写;回调方法签名与语义见上文"事件回调体系"表格。典型覆写:onConnection(UI 状态刷新)、onBtDiscovery(扫描列表)、onReceiveData(裸数据/协议透传)、onError(错误提示)。
失败模式、边界情况与并发
- 权限缺失:所有连接/断开/查询入口首先校验
ConnectUtil.isHasConnectPermission(mContext),不满足直接返回 false,避免运行时崩溃;BluetoothHelper类级标注@SuppressLint("MissingPermission"),权限由调用方在 UI 流程保证。 - MTU 回调丢失:
startChangeMtu设 5 秒超时任务(ChangeBleMtuTimeoutTask),回调未到则超时处理;重入时直接返回 true 并告警,防止并发重复请求。 - BLE→SPP 切换失败:
onFailed立即触发MSG_CHECK_BLE_DISCONNECT兜底;成功但固件未断 BLE 时由 3 秒延迟消息强制断开,避免双链路并存导致收发错乱。 - 设备被其他手机连接:
syncEdrConnectionStatus检测到edrStatus为已连接但本机 profile 未连接时,判定"被其他手机占用",尝试startConnectByBreProfiles抢占,失败仅打日志不阻塞主流程。 - 并发与线程:
instance为volatile且初始化走 synchronized 块(DCL);mAuthDeviceMap、needUpdateDeviceMap、bleToSppList均为普通HashMap/ArrayList,SDK 回调与主线程 Handler 操作存在潜在竞争,但工程上回调串行化于主线程(mHandler基于Looper.getMainLooper()),实际风险可控。 - 资源泄漏防护:单例持有
Context用@SuppressLint("StaticFieldLeak")标注并取Application级 context;destroy()清空监听器、Map 与 Handler 消息,供应用退出时调用。 - 未授权设备:开启设备认证时,链路已通但
isConnectedDevice()返回 false,业务层应等待认证回调后再展示可用状态,避免对未认证设备下发指令。
性能与运维
- 回连策略:构造时即
fastConnect(),减少用户手动重连等待;连接成功后再协商 MTU(BLE_MTU_MAX)提升单包吞吐,为健康数据同步提速。 - 日志可观测:
HealthApplication统一配置bluetooth_connect与 RCSP 层日志开关,并重定向到JL_Log::addLogOutput;连接库关键路径(JL_Log.d/i/w打点:连接方式、MTU 结果、切换等待、抢连结果)均可按 TAG 检索。 - 超时保护:所有异步协商(MTU、BLE 断开兜底)均有主线程 Handler 超时兜底,防止挂起。
扩展点
- 新增连接协议/设备类型:在
connectDevice(device, scanMessage)的协议决策分支中扩展(参照 SPP 特殊路径与 OTA 分支),决策结果仍是mBluetoothOp.connectBtDevice单一出口。 - 自定义事件消费者:实现/继承
BluetoothEventListener并注册到BluetoothHelper,不修改连接库即可订阅任意事件子集。 - 认证策略替换:
RcspAuth通过构造器注入数据发送函数(this::sendDataToDevice)与认证监听器,可替换为自定义认证实现;认证开关由ConfigHelper.isEnableDeviceAuth()控制。 - SDK 层定制:
getBluetoothOp()暴露BluetoothManager,高级场景可绕过门面直接操作 SDK(如自定义扫描过滤、多设备支持需同时调整setUseMultiDevice)。
测试
WatchManagerTest(WatchManagerTest.java)通过继承BluetoothEventListener覆写onRcspInit、onMandatoryUpgrade、onWatchSystemException等回调,验证连接库与 RCSP 协议层的协作契约及系统异常码上报路径。ConnectDebug(ConnectDebug.java)导入JL_Log用于连接调试,反映 SDK 日志 API 的测试用法。- 测试代码同时揭示了库的关键使用契约:连接回调必须先于协议指令下发、认证通过后才允许业务操作、强升设备需走
onMandatoryUpgrade特殊流程。
相关链接
- BluetoothHelper.java — 连接库门面实现
- BluetoothEventListener.java — 事件监听抽象
- BtEventCbManager.java — 事件分发管理器
- NeedUpdateDevice.java — OTA 待更新设备信息模型
- HealthApplication.java — 应用初始化(SDK 日志配置)
- WatchManagerTest.java — 连接/协议协作测试
- 相关能力页:WatchManager(手表管理)、ConfigHelper(配置中心)、设备升级(OTA)