蓝牙扫描与连接管理
蓝牙扫描与连接管理是 otasdk 中面向 OTA 业务层的蓝牙操作门面(Facade),通过 BluetoothHelper 单例统一封装 BLE 与经典蓝牙 SPP 两种链路的设备扫描、连接、断开、重连与数据读写,并通过 BTEventCbHelper 以观察者模式将底层 BleManager / SppManager 事件统一回调到主线程,供 UI 层订阅。
Purpose and Scope
本页覆盖 com.jieli.otasdk.tool.bluetooth 包下的两个核心类:
BluetoothHelper—— 对外提供的蓝牙操作统一入口(扫描、连接、读写、状态查询、回调注册);BTEventCbHelper—— 蓝牙事件的注册/分发辅助类,负责线程切换与多订阅者广播。
同时会说明它们与底层传输层(BleManager、SppManager)、配置层(ConfigHelper)、公共常量(OtaConstant、BluetoothConstant)之间的协作关系。
以下主题属于兄弟页面、不在本页展开:底层 BLE GATT 传输实现(BleManager 的扫描细节、MTU 协商、分包收发)与 SPP 套接字传输实现(SppManager 的 UUID 通道管理)、配置项来源(ConfigHelper)、以及基于本门面之上的 OTA 升级流程(BluetoothViewModel、BaseBluetoothFragment 等 UI 层用法)。
概述
设计目标
OTA 升级依赖一条稳定的蓝牙数据链路。杰理 OTA SDK 需要同时支持两种传输方式:
- BLE(低功耗蓝牙):走 GATT 服务与特征值,适合低功耗、短数据包的升级场景;
- SPP(经典蓝牙串口协议):走 RFCOMM 通道,适合大数据量、连续流的升级场景。
BluetoothHelper 的核心价值在于:屏蔽底层差异,向上层暴露一套统一的蓝牙操作 API。上层(BluetoothViewModel、BaseBluetoothFragment、OTA 流程代码)只需调用 startScan、connectDevice、writeDataToDevice 等方法,由门面根据 ConfigHelper 中的连接方式配置自动路由到 BleManager 或 SppManager。
关键概念
| 概念 | 说明 |
|---|---|
| 连接方式(ConnectWay) | ConfigHelper.getConnectWay() 决定路由:SPP / GATT_OVER_BR_EDR / 默认 BLE |
| 协议类型 | OtaConstant.PROTOCOL_BLE 与 OtaConstant.PROTOCOL_SPP,用于回调中区分事件来源 |
| 事件回调 | OnBTEventCallback 接口,覆盖适配器状态、扫描发现、连接状态、数据接收、MTU 变化 |
| 线程模型 | 底层事件在任意线程触发,BTEventCbHelper 统一投递到主线程 Handler 执行 |
使用场景
- 设备发现:调用
startScan(timeout)扫描附近杰理设备,通过onDiscovery回调获取BluetoothDevice与BleScanInfo(含 RSSI); - 建立连接:对扫描到的设备调用
connectDevice(device),监听onDeviceConnection确认连接成功; - 数据交互:连接成功后调用
writeDataToDevice()下发 OTA 命令,通过onReceiveData接收设备响应; - 状态监控:通过
isConnected()/isScanning()/isConnecting()轮询或通过回调驱动 UI 状态。
架构
flowchart TD
subgraph sg_UI["UI / 业务层"]
ViewModel["BluetoothViewModel / BaseBluetoothFragment"]
Callback["OnBTEventCallback 实现"]
end
subgraph sg_Facade["蓝牙门面层 (tool.bluetooth)"]
Helper["BluetoothHelper (单例)"]
CbHelper["BTEventCbHelper (事件分发)"]
end
subgraph sg_Transport["传输层 (tool.ota)"]
Ble["BleManager (BLE GATT)"]
Spp["SppManager (SPP RFCOMM)"]
end
subgraph sg_Config["配置层"]
Config["ConfigHelper"]
end
ViewModel -->|"startScan / connectDevice / writeDataToDevice"| Helper
ViewModel -->|"registerCallback"| CbHelper
Callback -->|"回调订阅"| CbHelper
Helper -->|"路由: isBleWay / isSppWay"| Config
Helper -->|"委托扫描/连接/读写"| Ble
Helper -->|"委托扫描/连接/读写"| Spp
Helper -->|"注册底层事件"| CbHelper
Ble -->|"onDiscoveryBle / onBleConnection / onBleDataNotification"| Helper
Spp -->|"onDiscoveryDevice / onSppConnection / onReceiveSppData"| Helper
CbHelper -->|"onDiscovery / onDeviceConnection / onReceiveData (主线程)"| Callback
架构说明:
BluetoothHelper是唯一对外的门面,持有BleManager、SppManager、BTEventCbHelper、ConfigHelper四个依赖(见 BluetoothHelper.kt)。它不直接操作 GATT 或 Socket,所有底层动作都委托给传输层管理器。BTEventCbHelper是事件总线:底层BleEventCallback/SppEventCallback的事件先到达BluetoothHelper的匿名回调对象,再转发给BTEventCbHelper,由其统一广播给所有注册的OnBTEventCallback(见 BTEventCbHelper.kt)。- 传输层(
BleManager/SppManager)各自实现扫描与连接细节,本页只描述门面对它们的调用契约。
单例与生命周期
BluetoothHelper 采用经典的线程安全懒加载单例:@Volatile 字段 + synchronized 双重检查(见 BluetoothHelper.kt)。构造时(init 块)同时向 BleManager 与 SppManager 注册事件回调,因此无论当前配置走哪种协议,两个传输层的事件都会被监听;destroy() 时对称地注销回调并释放资源、将 instance 置空以允许重新创建(见 BluetoothHelper.kt)。
双协议路由设计
门面层的核心决策逻辑全部委托给 ConfigHelper,自身只做分发。三个关键路由方法:
1. 链路选择:isBleWay() / isSppWay()
fun isScanning(): Boolean = if (configHelper.isBleWay()) {
bleManager.isBleScanning
} else {
sppManager.isScanning
}
fun getConnectedDevice(): BluetoothDevice? {
return if (configHelper.isSppWay()) {
sppManager.connectedSppDevice
} else {
bleManager.connectedBtDevice
}
}
Source: BluetoothHelper.kt
设计意图:isScanning() 优先判断 isBleWay(),而 getConnectedDevice() 优先判断 isSppWay()。这并非笔误,而是反映了默认策略:未显式配置时按 BLE 链路处理,但若配置为 SPP 则以 SPP 为准。所有状态查询方法(isScanning、isConnecting、getConnectedDevice、getConnectingDevice、getConnectedGatt、getBleMtu)都遵循这一"先查 SPP、否则走 BLE"的对称模式,保证上层代码不感知底层协议。
2. 连接路由:connectDevice() 三分支
fun connectDevice(device: BluetoothDevice?): Boolean =
when (configHelper.getConnectWay()) {
BluetoothConstant.PROTOCOL_TYPE_SPP -> sppManager.connectSpp(device)
BluetoothConstant.PROTOCOL_TYPE_GATT_OVER_BR_EDR -> bleManager.connectBleDevice(
device, BleConnectParam()
.setRequestMtu(BluetoothConstant.BLE_MTU_MAX)
.setTransport(BleConnectParam.TRANSPORT_BREDR)
)
else -> bleManager.connectBleDevice(device)
}
Source: BluetoothHelper.kt
设计意图:连接动作被拆成三种方式:
PROTOCOL_TYPE_SPP→ 走经典蓝牙 RFCOMM;PROTOCOL_TYPE_GATT_OVER_BR_EDR→ 走 BR/EDR 传输上的 GATT(TRANSPORT_BREDR),此时显式请求最大 MTU(BLE_MTU_MAX),适合大包升级;- 其他(默认 BLE)→ 走低功耗 GATT,参数使用默认值。
BleConnectParam 以链式 API 构造连接参数,将 MTU 请求与传输类型编码进一次调用,避免为每种组合定义独立方法。
扫描流程
fun startScan(timeout: Long): Boolean {
return if (configHelper.isBleWay()) {
bleManager.startLeScan(timeout)
} else {
sppManager.startDeviceScan(timeout)
}
}
fun stopScan() {
if (configHelper.isBleWay()) {
bleManager.stopLeScan()
} else {
sppManager.stopDeviceScan()
}
}
Source: BluetoothHelper.kt
扫描结果通过两条路径回传:
- BLE:
onDiscoveryBle(device, bleScanMessage),携带BleScanInfo(扫描信息,如广播数据、RSSI); - SPP:
onDiscoveryDevice(device, rssi),仅携带 RSSI,由门面包装成BleScanInfo().setRssi(rssi)以统一回调签名(见 BluetoothHelper.kt)。
扫描开始/结束状态统一通过 onDiscoveryChange(bStart, scanType) 通知,scanType 区分 PROTOCOL_BLE / PROTOCOL_SPP,UI 层可据此显示"搜索中"提示。
数据读写
writeDataToDevice() 是门面中最复杂的自适应方法:它先探测设备当前实际处于哪条链路,再选择对应的异步写接口:
fun writeDataToDevice(bluetoothDevice: BluetoothDevice?, byteArray: ByteArray?): Boolean {
if (null == bluetoothDevice || null == byteArray || byteArray.isEmpty()) return false
if (bleManager.isConnectedDevice(bluetoothDevice)) {//目前连接的设备是ble
bleManager.writeDataByBleAsync(
bluetoothDevice,
BleManager.BLE_UUID_SERVICE,
BleManager.BLE_UUID_WRITE,
byteArray
) { device, _, _, result, data ->
JL_Log.d(TAG, "writeDataByBleAsync",
"device : ${printDeviceInfo(device)}, result : $result,\n" +
"data : [${CHexConver.byte2HexStr(data)}]")
}
return true
}
if (sppManager.isSppConnected(bluetoothDevice)) {//目前连接的设备是Spp
sppManager.writeDataToSppAsync(
bluetoothDevice,
SppManager.UUID_SPP,
byteArray
) { device, sppUUID, result, data ->
JL_Log.d(TAG, "writeDataToSppAsync",
"device : ${printDeviceInfo(device)}, uuid : $sppUUID, result : $result,\n" +
"data : ${CHexConver.byte2HexStr(data)}")
}
return true
}
return false
}
Source: BluetoothHelper.kt
设计意图与边界行为:
- 前置校验:设备或数据为空、数据为空数组时直接返回
false,不产生无效调用; - 不依赖配置、依赖实际连接状态:即使配置为 BLE,只要该设备实际是通过 SPP 连接的(例如双模设备),也会正确路由到 SPP 通道;反之亦然。这避免了"配置与实况不一致"导致写入失败;
- 异步写(
...Async)携带回调,回调中打印设备信息、结果与十六进制数据,便于 OTA 调试; - 两条链路都未连接时返回
false,调用方(OTA 流程)应据此判定链路异常并提示用户。
事件回调机制
双层转发结构
底层事件 → BluetoothHelper 匿名回调 → BTEventCbHelper → 所有订阅者,共两层转发。第一层在 BluetoothHelper 的 bleEventCallback / sppEventCallback 中完成协议归一化:
private val bleEventCallback = object : BleEventCallback() {
override fun onDiscoveryBle(device: BluetoothDevice?, bleScanMessage: BleScanInfo?) {
btEventCbHelper.onDiscovery(device, bleScanMessage)
}
override fun onBleConnection(device: BluetoothDevice?, status: Int) {
btEventCbHelper.onDeviceConnection(device, OtaConstant.PROTOCOL_BLE, status)
}
override fun onBleDataNotification(
device: BluetoothDevice?, serviceUuid: UUID?,
characteristicsUuid: UUID?, data: ByteArray?
) {
btEventCbHelper.onReceiveData(device, OtaConstant.PROTOCOL_BLE, characteristicsUuid, data)
}
}
Source: BluetoothHelper.kt
归一化规则:BLE/SPP 各自的连接回调统一为 onDeviceConnection(device, way, status);数据回调统一为 onReceiveData(device, way, uuid, data);MTU 变化统一为 onBleMtuChange。way 参数让订阅者可以在一个回调里区分事件来源协议。
SPP 多通道过滤
SPP 链路支持自定义多通道,当开启多通道且当前回调 UUID 不是自定义通道时,该连接事件被主动丢弃,避免干扰业务层:
override fun onSppConnection(device: BluetoothDevice?, uuid: UUID?, status: Int) {
if (status == BluetoothProfile.STATE_CONNECTED && configHelper.isUseMultiSppChannel()
&& UUID.fromString(configHelper.getCustomSppChannel()) != uuid
) {
JL_Log.i(TAG, "onSppConnection", "skip custom uuid = $uuid")
return
}
btEventCbHelper.onDeviceConnection(device, OtaConstant.PROTOCOL_SPP, status)
}
Source: BluetoothHelper.kt
设计意图:某些杰理设备可能同时在多个 SPP UUID 上建连,业务层只关心自定义通道的连接事件;若不过滤,UI 可能被非业务通道的连接状态刷新干扰。
主线程投递与并发安全
BTEventCbHelper 内部维护 MutableList<OnBTEventCallback> 与主线程 Handler。所有事件的最终分发都经过 callbackEvent():
private fun callbackEvent(impl: CallbackImpl<OnBTEventCallback>?) {
if (null == impl) return
val runnable = CallbackRunnable(callbacks, impl)
if (Thread.currentThread().id == Looper.getMainLooper().thread.id) {
runnable.run()
} else {
uiHandler.post(runnable)
}
}
Source: BTEventCbHelper.kt
CallbackRunnable 在真正遍历前对回调列表做快照拷贝(temp.addAll(callbacks) 后遍历 temp),因此回调中即使有订阅者注册/注销,也不会触发 ConcurrentModificationException;同时把遍历放在主线程,保证 UI 更新无需再手动切换线程(见 BTEventCbHelper.kt)。
核心流程
sequenceDiagram
participant UI as 业务层 (BluetoothViewModel)
participant H as BluetoothHelper (门面)
participant B as BleManager / SppManager
participant CB as BTEventCbHelper
participant DEV as 杰理设备
UI->>H: startScan(timeout)
H->>B: startLeScan / startDeviceScan
B-->>CB: onDiscoveryBle / onDiscoveryDevice
CB-->>UI: onDiscovery(device, BleScanInfo) [主线程]
UI->>H: connectDevice(device)
H->>H: getConnectWay() 三分支路由
H->>B: connectSpp / connectBleDevice(BleConnectParam)
B-->>CB: onBleConnection / onSppConnection (status)
CB-->>UI: onDeviceConnection(device, way, status) [主线程]
UI->>H: writeDataToDevice(device, data)
H->>H: isConnectedDevice 探测实际链路
H->>B: writeDataByBleAsync / writeDataToSppAsync
B-->>CB: onBleDataNotification / onReceiveSppData
CB-->>UI: onReceiveData(device, way, uuid, data) [主线程]
流程要点:
- 扫描:UI 调用
startScan(timeout),门面按isBleWay()路由;每个发现的设备经"底层回调 → 门面归一化 →BTEventCbHelper→ 主线程"四跳到达 UI; - 连接:
connectDevice()按getConnectWay()三分支路由;SPP 场景下非自定义通道的连接事件在门面层被过滤; - 读写:
writeDataToDevice()不信任配置,而是用isConnectedDevice/isSppConnected探测设备实际连接链路,保证写入落到正确通道; - 线程:所有回调最终都在主线程执行,UI 层可直接更新界面。
使用示例
基本用法:注册回调并扫描连接
以下代码演示 SDK 上层(如 BluetoothViewModel 类)如何使用 BluetoothHelper 的完整链路——注册回调、启动扫描、连接设备:
class BluetoothViewModel {
// 通过门面单例获取统一操作入口
private val bluetoothHelper = BluetoothHelper.getInstance()
private val callback = object : OnBTEventCallback() {
override fun onDiscoveryChange(bStart: Boolean, scanType: Int) {
// 扫描状态变化,可刷新 UI 的"搜索中"提示
}
override fun onDiscovery(device: BluetoothDevice?, bleScanMessage: BleScanInfo?) {
// 发现设备:bleScanMessage 包含 RSSI 等广播信息
}
override fun onDeviceConnection(device: BluetoothDevice?, way: Int, status: Int) {
// way = OtaConstant.PROTOCOL_BLE 或 PROTOCOL_SPP
// status 为连接状态,可用于判断连接成功/断开
}
override fun onReceiveData(device: BluetoothDevice?, way: Int, uuid: UUID?, data: ByteArray?) {
// 设备主动上报的数据,OTA 流程在此解析应答
}
}
init {
bluetoothHelper.registerCallback(callback)
bluetoothHelper.startScan(5000L) // 扫描 5 秒
}
fun onDeviceSelected(device: BluetoothDevice?) {
bluetoothHelper.stopScan()
bluetoothHelper.connectDevice(device) // 按配置自动路由 BLE/SPP
}
fun release() {
bluetoothHelper.unregisterCallback(callback)
bluetoothHelper.destroy()
}
}
Source: BluetoothHelper.kt 与 BTEventCbHelper.kt
高级用法:按配置路由的连接与自适应写入
connectDevice 的 when 分发与 writeDataToDevice 的实际链路探测,是上层无需感知协议差异的关键:
fun connectDevice(device: BluetoothDevice?): Boolean =
when (configHelper.getConnectWay()) {
BluetoothConstant.PROTOCOL_TYPE_SPP -> sppManager.connectSpp(device)
BluetoothConstant.PROTOCOL_TYPE_GATT_OVER_BR_EDR -> bleManager.connectBleDevice(
device, BleConnectParam()
.setRequestMtu(BluetoothConstant.BLE_MTU_MAX)
.setTransport(BleConnectParam.TRANSPORT_BREDR)
)
else -> bleManager.connectBleDevice(device)
}
fun disconnectDevice(device: BluetoothDevice?) {
if (configHelper.isSppWay()) {
sppManager.disconnectSpp(device, null)
} else {
bleManager.disconnectBleDevice(device)
}
}
Source: BluetoothHelper.kt
要点:disconnectDevice 直接按 isSppWay() 分流(无回退),因为断开动作不需要探测实况;而连接与写入则更谨慎地做了配置路由 + 实况探测的双保险。
配置选项
蓝牙连接行为由 ConfigHelper 驱动,BluetoothHelper 消费以下配置(配置项实际定义在 ConfigHelper 中,本页仅列出门面依赖的关键项):
| 配置项(方法) | 类型 | 默认行为 | 影响 |
|---|---|---|---|
getConnectWay() | Int | 默认 BLE | 决定 connectDevice 三分支路由(SPP / GATT_OVER_BR_EDR / BLE) |
isBleWay() | Boolean | 未配置时按 BLE | 决定扫描、状态查询走 BleManager 还是 SppManager |
isSppWay() | Boolean | false | 决定断开、设备查询走 SPP 链路 |
isUseMultiSppChannel() | Boolean | false | 是否启用 SPP 多通道过滤 |
getCustomSppChannel() | String | 自定义 UUID 字符串 | 多通道模式下唯一被放行的 SPP 连接 UUID |
配置与行为的对应关系:
- 扫描(
startScan/stopScan)只认isBleWay(); - 连接(
connectDevice)只认getConnectWay()的三分支; - 断开与已连接设备查询认
isSppWay(); - SPP 连接事件过滤同时依赖
isUseMultiSppChannel()与getCustomSppChannel()。
API 参考
BluetoothHelper(com.jieli.otasdk.tool.bluetooth)
| 方法 | 签名要点 | 说明 |
|---|---|---|
getInstance() | BluetoothHelper(伴生对象静态方法) | 线程安全懒加载单例 |
destroy() | Unit | 注销回调、销毁传输层、置空单例 |
registerCallback(cb) | Unit | 注册 OnBTEventCallback 订阅者(自动去重) |
unregisterCallback(cb) | Unit | 注销订阅者 |
isConnected() | Boolean | 是否有已连接设备 |
isDeviceConnected(device) | Boolean | 指定设备是否已连接 |
isScanning() | Boolean | 是否正在扫描(按 isBleWay() 路由) |
isConnecting() | Boolean | 是否正在连接 |
getConnectedDevice() | BluetoothDevice? | 当前已连接设备(按 isSppWay() 路由) |
getConnectingDevice() | BluetoothDevice? | 正在连接的设备 |
getConnectedGatt(device) | BluetoothGatt? | BLE 链路的 GATT 对象(SPP 返回 null) |
getBleMtu(device) | Int | 当前 MTU(SPP 返回 BLE_MTU_MAX) |
startScan(timeout: Long) | Boolean | 启动扫描,超时自动停止 |
stopScan() | Unit | 停止扫描 |
connectDevice(device) | Boolean | 按 getConnectWay() 三分支建立连接 |
connectBleDevice(device) | Boolean | 强制走 BLE 连接 |
disconnectDevice(device) | Unit | 断开连接(按 isSppWay() 分流) |
reconnectDevice(address, isUseNewAdv) | Unit | 按 MAC 地址重连 BLE(SPP 暂未实现,代码中留有 TODO) |
writeDataToDevice(device, data) | Boolean | 按实际链路异步写数据;未连接/参数非法返回 false |
BTEventCbHelper(事件分发)
| 方法 | 说明 |
|---|---|
registerCallback(cb) | 添加订阅者(已存在则忽略) |
unregisterCallback(cb) | 移除订阅者(空列表时直接返回) |
release() | 清空订阅者并移除主线程 Handler 中所有待执行消息 |
callbackEvent(impl) | 内部方法:在主线程执行 CallbackRunnable |
OnBTEventCallback 事件接口
| 回调 | 参数 | 触发时机 |
|---|---|---|
onAdapterChange | bEnabled: Boolean | 蓝牙适配器开关状态变化 |
onDiscoveryChange | bStart: Boolean, scanType: Int | 扫描开始/结束 |
onDiscovery | device, bleScanMessage: BleScanInfo? | 发现设备(SPP 由门面包装 RSSI) |
onDeviceConnection | device, way: Int, status: Int | 连接状态变化(way 区分 BLE/SPP) |
onReceiveData | device, way: Int, uuid: UUID?, data: ByteArray? | 收到设备数据 |
onBleMtuChange | device, mtu: Int, status: Int | BLE MTU 协商结果 |
失败模式、边界情况与并发
写入失败的三类场景
writeDataToDevice() 返回 false 的三种情况及其业务含义:
- 参数非法:
device为 null、byteArray为 null 或空数组——调用方应先做非空校验; - 双链路均未连接:设备既不在
BleManager.isConnectedDevice也不在SppManager.isSppConnected中——表明连接已丢失,OTA 流程应进入重连或失败分支; - 写回调
result=false:链路在但写入失败(如 MTU 不足、特征值不可写、远端断开)——异步回调中会打印设备信息与十六进制数据,便于定位。
配置与实况不一致
门面刻意区分"配置路由"与"实况探测":
- 信任配置:扫描、连接、断开、状态查询按
ConfigHelper分流; - 信任实况:写入按设备实际连接链路分流。
这种双轨策略避免了"配置为 BLE 但设备实际通过 SPP 连接"时写入丢失,是 OTA 双模设备场景下的关键容错设计。
并发与线程安全
- 单例创建:
@Volatile+synchronized双重检查,保证多线程首次获取时只构造一次(见 BluetoothHelper.kt); - 回调列表遍历:
CallbackRunnable先temp.addAll(callbacks)快照再遍历,注册/注销并发修改不会抛ConcurrentModificationException(见 BTEventCbHelper.kt)。注意快照意味着本次分发不包含分发过程中新注册的回调,这是可接受的最终一致性语义; - 主线程约束:事件必然在主线程回调,UI 更新安全;但业务层若在回调中执行耗时操作会阻塞主线程,OTA 大包解析应自行切线程。
边界情况
- SPP 自定义通道过滤:开启多通道后,非
getCustomSppChannel()的 UUID 连接事件被return丢弃;此时订阅者不会收到任何 SPP 连接通知——业务层需知道"无通知即非目标通道"; - SPP 重连缺口:
reconnectDevice()在 SPP 分支中仅有//TODO:需要增加SPP自定义回连方式注释,即SPP 按地址重连当前未实现,仅 BLE 支持; - BR/EDR GATT 连接:
GATT_OVER_BR_EDR分支强制TRANSPORT_BREDR并要求最大 MTU,若设备不支持 BR/EDR GATT,连接会失败——这是配置与设备能力的契约,调用方应提供配置入口让用户切换。
性能与运维建议
- 扫描超时:
startScan(timeout)由传输层管理超时自动停止,上层应配合onDiscoveryChange(bStart=false)刷新 UI,避免无限扫描耗电; - 日志辅助:门面在连接事件、读写回调中均打印设备信息与十六进制数据(
printDeviceInfo/CHexConver.byte2HexStr),线上问题可通过BluetoothHelperTAG 过滤日志; - MTU 协商:BLE 链路 MTU 通过
getBleMtu()查询、onBleMtuChange感知;大包写入前应确认 MTU,减少分包数可显著提升 OTA 吞吐; - 生命周期:页面销毁时应成对调用
unregisterCallback与destroy();destroy()会置空单例,若后续仍使用需重新getInstance(),注意避免在旧引用上操作。
扩展点
- 自定义连接参数:通过
BleConnectParam链式 API(setRequestMtu、setTransport、TRANSPORT_BREDR)扩展 BLE 连接行为;如需新增传输类型,可在connectDevice的when分支中扩展; - 新增事件类型:在
OnBTEventCallback增加方法,并在BTEventCbHelper中按现有callbackEvent(impl)模式实现对应分发即可,分发基础设施(主线程 + 快照)无需改动; - SPP 多通道:通过
ConfigHelper的isUseMultiSppChannel()/getCustomSppChannel()控制通道过滤策略,未来可扩展为多通道白名单; - SPP 自定义回连:
reconnectDevice的 TODO 位置是预留的扩展点,可在SppManager增加按地址重连 API 后在此接入。
相关链接
- BluetoothHelper.kt(门面实现)
- BTEventCbHelper.kt(事件分发实现)
- 底层传输层:
BleManager(com.jieli.otasdk.tool.ota.ble)与SppManager(com.jieli.otasdk.tool.ota.spp)——BLE GATT 与 SPP 通道的扫描/连接/收发细节,见对应 OTA 传输文档 - 配置来源:
ConfigHelper(com.jieli.otasdk.tool.config)——连接方式、SPP 通道等配置项的读写 - UI 层使用示例:
BluetoothViewModel、BaseBluetoothFragment(com.jieli.otasdk.ui)——本门面的典型消费方 - 公共常量:
OtaConstant.PROTOCOL_BLE/PROTOCOL_SPP、BluetoothConstant.PROTOCOL_TYPE_SPP/GATT_OVER_BR_EDR/BLE_MTU_MAX