在物联网和智能硬件快速发展的今天,BLE(蓝牙低功耗)技术已经成为移动设备与周边配件通信的主流方案之一。作为一名在Android蓝牙开发领域踩过无数坑的老兵,我深知构建一个稳定的BLE连接管理器对项目成败的关键影响。
去年在为某医疗设备厂商开发远程监护App时,我们团队曾因BLE连接不稳定问题导致数据丢失,差点造成严重后果。正是这次经历促使我沉淀出这套经过实战检验的Kotlin版BLE连接管理器骨架。与上篇侧重基础架构不同,下篇将聚焦三个核心痛点:
这套方案已在智能家居、医疗设备、运动手环等多个领域落地,最长的设备持续连接时间达到72天无断连。下面我就从实现原理到代码细节,完整拆解这个工业级BLE连接管理器的核心实现。
稳定的BLE连接必须建立明确的状态管理机制。我们定义6个核心状态:
kotlin复制enum class ConnectionState {
DISCONNECTED, // 初始状态
CONNECTING, // 连接中
CONNECTED, // 已连接但服务未发现
SERVICE_DISCOVERED, // 服务已发现
READY, // 所有特征准备就绪
DISCONNECTING // 主动断开中
}
状态转换需要特别注意几个边界情况:
在状态变更时采用同步锁保证线程安全:
kotlin复制private val stateLock = Any()
fun updateState(newState: ConnectionState) {
synchronized(stateLock) {
if (currentState == newState) return
val validTransition = when {
currentState == DISCONNECTED && newState == CONNECTING -> true
currentState == CONNECTING && newState in listOf(CONNECTED, DISCONNECTED) -> true
// 其他合法转换判断...
else -> false
}
if (!validTransition) {
logError("Invalid state transition: $currentState -> $newState")
return
}
currentState = newState
notifyStateChanged()
}
}
关键经验:状态变更必须做有效性校验,我们曾因漏判DISCONNECTING到CONNECTING的非法转换导致连接池泄漏。
BLE的MTU限制(通常20字节)要求大数据分包发送。我们实现带优先级的写入队列:
kotlin复制class WriteQueue {
private val highPriorityQueue = LinkedList<ByteArray>()
private val normalPriorityQueue = LinkedList<ByteArray>()
private var isWriting = false
fun addData(data: ByteArray, highPriority: Boolean = false) {
synchronized(this) {
if (highPriority) {
highPriorityQueue.add(data)
} else {
normalPriorityQueue.add(data)
}
processNext()
}
}
private fun processNext() {
if (isWriting) return
val data = highPriorityQueue.poll() ?: normalPriorityQueue.poll() ?: return
isWriting = true
bleGatt?.run {
writeCharacteristic(buildCharacteristic(data))?.also { success ->
if (!success) {
handler.postDelayed({ processNext() }, WRITE_RETRY_DELAY)
}
}
}
}
fun onWriteComplete() {
isWriting = false
processNext()
}
}
在接收端实现校验机制:
kotlin复制private var packetBuffer = ByteArray(0)
fun onDataReceived(data: ByteArray) {
// 检查头尾标志位
if (data[0] != START_FLAG || data.last() != END_FLAG) {
requestResend()
return
}
// 合并数据包
packetBuffer += data.drop(1).dropLast(1)
// 检查CRC校验
if (calculateCRC(packetBuffer) != expectedCRC) {
packetBuffer = ByteArray(0)
requestResend()
return
}
processCompletePacket(packetBuffer)
packetBuffer = ByteArray(0)
}
通过连接池管理多个设备连接:
kotlin复制class BleConnectionPool(
private val maxConnections: Int = 3
) {
private val activeConnections = mutableMapOf<String, BleDeviceHandler>()
private val pendingQueue = LinkedList<String>()
fun connectDevice(macAddress: String) {
when {
activeConnections.size < maxConnections -> {
val handler = BleDeviceHandler(macAddress)
activeConnections[macAddress] = handler
handler.connect()
}
activeConnections.containsKey(macAddress) -> {
// 已连接设备忽略重复请求
}
else -> {
if (!pendingQueue.contains(macAddress)) {
pendingQueue.add(macAddress)
}
}
}
}
fun onDeviceDisconnected(macAddress: String) {
activeConnections.remove(macAddress)
processNextInQueue()
}
private fun processNextInQueue() {
if (pendingQueue.isNotEmpty() && activeConnections.size < maxConnections) {
connectDevice(pendingQueue.poll())
}
}
}
为不同设备类型设置优先级:
kotlin复制enum class DevicePriority {
CRITICAL, // 医疗设备等
HIGH, // 支付设备等
NORMAL // 普通传感器
}
fun getConnectionSlot(priority: DevicePriority): Int {
return when (priority) {
CRITICAL -> 1 // 保留槽位
HIGH -> (maxConnections * 0.6).toInt()
NORMAL -> maxConnections
}
}
kotlin复制sealed class BleError {
object Timeout : BleError()
object GattFailure : BleError()
object CharacteristicNotFound : BleError()
data class Custom(val code: Int) : BleError()
}
fun handleError(error: BleError) {
when (error) {
is Timeout -> {
if (retryCount.get() < MAX_RETRY) {
retryCount.incrementAndGet()
reconnect()
} else {
notifyFatalError()
}
}
is GattFailure -> {
releaseGattResources()
delay(RECONNECT_DELAY)
refreshGatt()
}
// 其他错误处理...
}
}
kotlin复制private val heartbeatChecker = object : Runnable {
override fun run() {
if (lastPacketTime.elapsed() > HEARTBEAT_THRESHOLD) {
sendHeartbeat().also {
if (!it) handleError(BleError.Timeout)
}
}
handler.postDelayed(this, HEARTBEAT_INTERVAL)
}
}
fun startHeartbeat() {
handler.removeCallbacks(heartbeatChecker)
handler.postDelayed(heartbeatChecker, HEARTBEAT_INTERVAL)
}
kotlin复制fun negotiateConnectionParameters() {
val params = ConnectionParameters(
interval = 15, // 1.25ms单位
latency = 0, // 无跳过事件
timeout = 500 // 超时10s
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
gatt.requestConnectionPriority(
BluetoothGatt.CONNECTION_PRIORITY_HIGH
)
} else {
// 通过反射调用隐藏API(需添加@SuppressLint)
try {
val method = gatt.javaClass.getMethod(
"requestConnectionPriority",
Int::class.javaPrimitiveType
)
method.invoke(gatt, params.priority)
} catch (e: Exception) {
logError("Failed to set connection priority", e)
}
}
}
kotlin复制override fun onDestroy() {
// 1. 移除所有回调
handler.removeCallbacksAndMessages(null)
// 2. 释放Gatt资源
bleGatt?.run {
disconnect()
close()
}
// 3. 清空观察者列表
observers.clear()
// 4. 停止扫描(如果正在扫描)
bluetoothAdapter.bluetoothLeScanner?.stopScan(scanCallback)
}
kotlin复制@Test
fun testStateTransition() {
val manager = createManager()
// 测试合法转换
manager.updateState(CONNECTING)
assertEquals(CONNECTING, manager.currentState)
// 测试非法转换
manager.updateState(READY)
assertNotEquals(READY, manager.currentState)
}
@Test
fun testWriteQueuePriority() {
val queue = WriteQueue()
queue.addData("normal".toByteArray())
queue.addData("high".toByteArray(), true)
assertEquals("high", String(queue.processNext()))
assertEquals("normal", String(queue.processNext()))
}
建议覆盖以下测试用例:
在智能家居项目中,我们发现当手机同时连接Wi-Fi和BLE设备时,2.4GHz频段干扰会导致RSSI值剧烈波动。最终通过以下调整显著改善稳定性:
kotlin复制fun getScanInterval(): Long {
return when (wifiManager.connectionInfo.rssi) {
in -80..0 -> 3000L // 强干扰环境
in -90..-80 -> 2000L
else -> 1000L
}
}
kotlin复制fun onCharacteristicWrite() {
writeQueue.onWriteComplete()
// 针对特定设备添加延迟
if (deviceModel == "SPECIAL_MODEL") {
handler.postDelayed({
writeQueue.processNext()
}, 80)
}
}
这套框架经过两年迭代,核心代码已在GitHub获得1200+星标,被多个开源项目引用。完整实现需要考虑具体业务场景调整参数,但核心架构已经过数十款设备验证。特别提醒:不同厂商的BLE芯片实现差异很大,建议在新设备接入时至少进行48小时压力测试。