You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复BLE 133 GATT连接超时问题?

解决BLE并行连接中的133 GATT CONN TIMEOUT错误

核心问题分析

频繁出现133 GATT连接超时,通常和蓝牙资源竞争、连接逻辑不合理、资源释放不彻底有关,结合你的代码,主要问题点包括:

  • 扫描与并行连接同时进行,导致蓝牙控制器资源过载
  • 连接失败后立即重试,短时间内重复请求占用有限的BLE连接槽
  • 资源释放逻辑存在延迟,未及时清理失效的BleControlManager实例
  • 用Handler轮询触发连接,时机控制不够精准,容易导致并发冲突

具体修复方案

1. 分离扫描与连接的资源占用

蓝牙控制器在扫描和连接时会抢占同一套射频资源,建议:

  • 把扫描模式从SCAN_MODE_LOW_POWER改为SCAN_MODE_BALANCED,平衡扫描效率和资源占用
  • 当并行连接数达到阈值时,暂停扫描,待有连接释放后再恢复

2. 优化连接重试逻辑

避免连接失败后立即重试,增加重试间隔并限制重试次数,防止无限循环消耗资源:

  • 给每个失败连接的设备添加重试次数记录,超过3次则加入黑名单不再尝试
  • 重试间隔从500ms延长至1-2秒,给蓝牙控制器足够的恢复时间

3. 确保BLE资源彻底释放

在断开连接后立即清理资源,避免无效实例占用连接槽:

  • 先调用close()再移除bleControlManagers中的记录
  • 同步清理重试计数等关联数据,避免残留无效状态

4. 改进并行连接触发逻辑

用协程替代Handler轮询,更精准地控制连接时机,避免并发冲突:

  • 用CoroutineScope的delay替代Handler.postDelayed,简化并发控制
  • 在扫描回调中仅添加设备到队列,由单独的协程循环处理队列连接

代码修改示例

调整扫描设置

private fun buildSettings() =
    ScanSettings.Builder()
        .setScanMode(ScanSettings.SCAN_MODE_BALANCED) // 改用平衡模式
        .build()

增加重试次数记录与限制

在ScanningService中添加重试计数:

private val retryCounts = mutableMapOf<String, Int>() // 记录设备重试次数
private const val MAX_RETRY_TIMES = 3 // 最大重试次数

// 在连接失败的fail回调中修改:
.fail { device, status ->
    Log.e(TAG, "Failed to connect to device ${device.name}: $status")
    errorMessage = "Failed to connect: $status"
    foundDevices.remove(device)
    if (!stopRequested && device.address !in checkedDevices.map { it.address }) {
        val currentRetry = retryCounts.getOrDefault(device.address, 0)
        if (currentRetry >= MAX_RETRY_TIMES) {
            bannedDevices.add(device) // 超过重试次数加入黑名单
            retryCounts.remove(device.address)
        } else {
            retryCounts[device.address] = currentRetry + 1
            if (!unCheckedDevices.contains(device))
                unCheckedDevices.add(device)
            deviceQueue.add(device)
        }
    }
    bleControlManagers.remove(device.address)
    bleControlManager.close()
    connectionToAnotherDevice()
}

用协程替代Handler轮询

修改connectionToAnotherDevice中的延迟逻辑:

// 替换Handler.postDelayed为协程delay
else if (bleControlManagers.size >= MAX_CONNECTIONS) {
    CoroutineScope(Dispatchers.Main).launch {
        delay(1000) // 延长至1秒
        connectionToAnotherDevice()
    }
}

优化资源释放逻辑

private fun disconnectAndCleanup(bleControlManager: BleControlManager, device: BluetoothDevice?) {
    CoroutineScope(Dispatchers.IO).launch {
        bleControlManager.disconnect().enqueue()
        bleControlManager.close() // 立即关闭资源
        device?.let {
            bleControlManagers.remove(it.address)
            retryCounts.remove(it.address) // 清理重试记录
        }
        deviceProcessor.setErrorMessage(null)
        connectionToAnotherDevice()
    }
}

扫描回调中避免频繁触发连接

在scanLeDevice启动时启动一个协程处理队列:

fun scanLeDevice(letter: String, start: Long, end: Long) {
    // ... 原有代码 ...
    CoroutineScope(Dispatchers.Main).launch {
        while (scanning.value) {
            if (bleControlManagers.size < MAX_CONNECTIONS && deviceQueue.isNotEmpty()) {
                connectionToAnotherDevice()
            }
            delay(300) // 定期检查队列
        }
    }
}

// 修改leScanCallback中的连接触发逻辑:
if (device.address !in bannedDevices.map { it.address } &&
    device.address !in deviceQueue.map { it.address } &&
    device.address !in foundDevices.map { it.address } &&
    device.address !in checkedDevices.map { it.address }) {
    Logger.d(TAG, "Device added to queue: $deviceName (${device.address})")
    deviceQueue.add(device)
    foundDevices.add(device)
    // 移除这里的connectionToAnotherDevice()调用
}

内容的提问来源于stack exchange,提问作者Артур Шитиков

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 03:35:54