Android_BLE开发——连接

本文结合 BLE 开发核心原理与实战经验,深入剖析 Android 蓝牙连接的实现细节,涵盖连接管理、连接超时控制、自动重连机制,助你打造简洁高效的实现方案。

一、连接核心流程架构

1.1、 连接状态机(示例代码)

kotlin 复制代码
sealed class ConnectionState {
    object Disconnected : ConnectionState()
    data class Connecting(val device: BluetoothDevice) : ConnectionState()
    data class Connected(val gatt: BluetoothGatt) : ConnectionState()
    data class ServicesDiscovered(val services: List<BluetoothGattService>) : ConnectionState()
    data class Error(val message: String) : ConnectionState()
}

1.2、连接管理器基础实现(示例代码)

kotlin 复制代码
class BleConnector(
    private val context: Context,
    private val scope: CoroutineScope
) {
    private var bluetoothGatt: BluetoothGatt? = null
    
    // 使用 Channel 管理连接事件
    private val connectionEventChannel = Channel<ConnectionState>()
    val connectionEvents = connectionEventChannel.receiveAsFlow()

    // 设备连接入口
    fun connect(device: BluetoothDevice) = scope.launch {
        connectionEventChannel.send(ConnectionState.Connecting(device))
        
        bluetoothGatt = device.connectGatt(
            context, 
            false, 
            object : BluetoothGattCallback() {
                override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
                    when (newState) {
                        BluetoothProfile.STATE_CONNECTED -> {
                            connectionEventChannel.trySend(ConnectionState.Connected(gatt))
                            gatt.discoverServices()
                        }
                        BluetoothProfile.STATE_DISCONNECTED -> {
                            connectionEventChannel.trySend(ConnectionState.Error("连接断开"))
                            close()
                        }
                    }
                }

                override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
                    gatt.services?.let { 
                        connectionEventChannel.trySend(ConnectionState.ServicesDiscovered(it))
                    }
                }
            }
        )
    }

    fun close() {
        bluetoothGatt?.disconnect()
        bluetoothGatt?.close()
        connectionEventChannel.close()
    }
}

二、连接优化策略

2.1、连接超时控制(示例代码)

kotlin 复制代码
private suspend fun connectWithTimeout(device: BluetoothDevice, timeout: Long) {
    try {
        withTimeout(timeout) {
            connect(device)
            // 等待连接完成
            connectionEvents.first { 
                it is ConnectionState.Connected || it is ConnectionState.Error 
            }
        }
    } catch (e: TimeoutCancellationException) {
        connectionEventChannel.send(ConnectionState.Error("连接超时"))
        close()
    }
}

2.2、自动重连机制(示例代码)

kotlin 复制代码
class AutoReconnect(
    private val connector: BleConnector,
    private val maxRetries: Int = 3
) {
    private var retryCount = 0

    fun enable() {
        connector.connectionEvents.onEach { state ->
            when (state) {
                is ConnectionState.Error -> {
                    if (retryCount < maxRetries) {
                        delay(2000)
                        connector.connect(state.device)
                        retryCount++
                    }
                }
                is ConnectionState.Connected -> retryCount = 0
                else -> Unit
            }
        }.launchIn(connector.scope)
    }
}

三、GATT 操作封装

3.1、特征值读写扩展函数(示例代码)

kotlin 复制代码
// 安全写入特征值
suspend fun BluetoothGatt.safeWriteCharacteristic(
    characteristic: BluetoothGattCharacteristic,
    data: ByteArray
): Boolean = suspendCancellableCoroutine { continuation ->
    characteristic.value = data
    writeCharacteristic(characteristic)
    
    val callback = object : BluetoothGattCallback() {
        override fun onCharacteristicWrite(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            status: Int
        ) {
            continuation.resume(status == BluetoothGatt.GATT_SUCCESS) {}
        }
    }
    
    continuation.invokeOnCancellation { 
        close() 
    }
}

3.2、通知订阅管理(示例代码)

kotlin 复制代码
fun BluetoothGatt.enableNotifications(
    characteristic: BluetoothGattCharacteristic
): Flow<ByteArray> = callbackFlow {
    val cccdUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
    
    // 启用通知
    setCharacteristicNotification(characteristic, true)
    
    // 配置 CCCD
    characteristic.getDescriptor(cccdUuid)?.let { descriptor ->
        descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
        writeDescriptor(descriptor)
    }

    // 监听数据变化
    val callback = object : BluetoothGattCallback() {
        override fun onCharacteristicChanged(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic
        ) {
            trySend(characteristic.value)
        }
    }
    
    awaitClose { 
        setCharacteristicNotification(characteristic, false)
        close()
    }
}

四、实战问题和解决方案

4.1、MTU 协商优化(示例代码)

kotlin 复制代码
private const val DEFAULT_MTU = 23
private const val MAX_MTU = 512

suspend fun BluetoothGatt.negotiateMtu(): Int = suspendCoroutine { continuation ->
    requestMtu(MAX_MTU)
    
    val callback = object : BluetoothGattCallback() {
        override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
            continuation.resume(mtu)
        }
    }
}

4.2、大数据分包处理(示例代码)

kotlin 复制代码
suspend fun BluetoothGatt.writeLargeData(
    characteristic: BluetoothGattCharacteristic,
    data: ByteArray,
    chunkSize: Int = 512
) {
    data.toList().chunked(chunkSize).forEachIndexed { index, chunk ->
        val chunkData = chunk.toByteArray()
        val isLastChunk = index == data.size / chunkSize
        
        // 添加分包标识
        val packet = if (isLastChunk) {
            chunkData + 0x00.toByte()
        } else {
            chunkData + 0x01.toByte()
        }
        
        safeWriteCharacteristic(characteristic, packet)
    }
}

五、最佳实践建议

5.1、生命周期绑定(示例代码)

kotlin 复制代码
class BleConnectionFragment : Fragment() {
    private val connector by lazy { 
        BleConnector(requireContext(), lifecycleScope) 
    }

    override fun onStart() {
        connector.connectionEvents
            .onEach { updateUi(it) }
            .launchIn(lifecycleScope)
    }

    override fun onStop() {
        connector.close()
    }
}

5.2、连接参数优化(示例代码)

kotlin 复制代码
fun optimizeConnectionParameters(gatt: BluetoothGatt) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH)
        gatt.requestMtu(512)
    }
}

5.3、错误统一处理(示例代码)

kotlin 复制代码
private fun handleGattError(status: Int): String = when (status) {
    BluetoothGatt.GATT_SUCCESS -> "操作成功"
    BluetoothGatt.GATT_READ_NOT_PERMITTED -> "无读取权限"
    BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> "无写入权限"
    BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION -> "认证失败"
    else -> "未知错误 (code: $status)"
}

六、调试与监控

6.1、连接状态监控

kotlin 复制代码
fun monitorConnectionQuality() {
    scope.launch {
        while (true) {
            bluetoothGatt?.readRemoteRssi()
            delay(5000)
        }
    }
}

override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) {
    Log.d("BLE", "当前信号强度: $rssi dBm")
}

6.2、使用调试工具

推荐工具:

  • nRF Connect:可视化查看 GATT 层级
  • Wireshark:抓包分析协议交互
  • Android Bluetooth HCI Log:查看底层日志

七、开发过程中需要注意的点

  • 开发的过程中要尽量避免133错误。

  • 如果连接调用只是挂起,没有发生超时onConnectionStateChange 回调也从未调用过。可能是外围设备快没电了或者是超出设备的可操作范围。

  • 确保在连接之前停止扫描。

  • 所有与连接和断开连接的相关调用都是 异步 的。蓝牙栈执行它们需要一些时间。避免在极短的时间做出大量BLE的相关操作。

建议结合 Android BLE KTX 官方扩展库进一步简化开发流程。

更多分享

  1. Android_BLE开发------初识BLE
  2. Android_BLE开发------扫描
  3. Android_BLE开发------读写
  4. Android_BLE开发------绑定
  5. Android_BLE开发------优化
相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
ai2work2 天前
ch23 综合复刻:从零做一个最小可用版本(capstone)
kotlin
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
ai2work2 天前
ch21 签名、校验与发版
kotlin
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui