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

Kotlin中BLE设备discoverServices()返回服务列表为空的问题排查求助

问题分析与解决方案

首先,你的代码里存在两个核心问题,直接导致了服务列表始终为空:

1. 误解了discoverServices()的异步特性

BluetoothGatt.discoverServices()是异步发起的操作,它返回的boolean值只是告诉你「请求是否成功提交给蓝牙栈」,而不是「服务已经发现完成」。真正的服务发现结果,必须通过BluetoothGattCallback的onServicesDiscovered()回调来接收——这是Android BLE框架规定的唯一正确时机。

2. 代码时序完全错误

看你的代码片段:

Handler(Looper.getMainLooper()).post {
    var temp = bluetoothGatt?.discoverServices()
    addGlog("discordservice() returned ${temp.toString()}")
}
addGlog("handler discover service reached an end")
val gattServices: List<BluetoothGattService> = gatt.getServices()
addGlog("Services count: " + gattServices.size)

这里的坑在于:如果当前代码已经在主线程执行,Handler.post()的任务会被放到主线程消息队列的末尾。也就是说,你先执行了后面的gatt.getServices(),然后才会执行post里的discoverServices()——这时候你获取的是发现服务之前的空列表,结果当然是0。

哪怕当前代码在子线程,discoverServices()发起后也需要几十到几百毫秒才能完成,你立刻同步调用getServices(),结果还是空的。


正确的实现方式

第一步:重写BluetoothGattCallback的回调方法

所有BLE操作的结果都要通过回调接收,服务发现也不例外:

private val gattCallback = object : BluetoothGattCallback() {
    // 先确保设备连接成功
    override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
        super.onConnectionStateChange(gatt, status, newState)
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            // 连接成功后,才发起服务发现
            gatt?.let {
                Handler(Looper.getMainLooper()).post {
                    val isInitiated = it.discoverServices()
                    addGlog("discoverServices() initiated: $isInitiated")
                }
            }
        }
    }

    // 服务发现完成的回调——这才是获取服务的正确时机
    override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
        super.onServicesDiscovered(gatt, status)
        if (status == BluetoothGatt.GATT_SUCCESS) {
            gatt?.let {
                val services = it.services
                addGlog("Services count: ${services.size}")
                services.forEach { service ->
                    addGlog("Service uuid ${service.uuid.toString()}")
                }
            }
        } else {
            addGlog("Service discovery failed, status code: $status")
        }
    }

    // 其他必要的回调(比如特征读写、通知等)按需实现
}

第二步:发起服务发现的正确姿势

不要在发起请求后立刻同步获取服务,而是等待onServicesDiscovered()回调触发。另外要注意:

  • 必须在设备成功连接后再调用discoverServices(),否则请求会失败
  • 确保BluetoothGatt对象在整个操作周期内有效,不要提前调用close()或disconnect()

额外排查点

如果按照上面的方式还是获取不到服务,可以检查这些地方:

  • 权限问题:Android 12+需要BLUETOOTH_CONNECT权限;Android 6-11需要ACCESS_FINE_LOCATION(因为BLE扫描依赖位置权限),并且要在运行时申请
  • 设备兼容性:部分BLE设备可能需要先写入特定的特征值或者开启通知才能暴露全部服务,但Realme Buds 2这类消费级耳机应该不需要
  • 蓝牙栈状态:有时候重启手机或BLE设备可以解决一些奇怪的蓝牙栈缓存问题

内容的提问来源于stack exchange,提问作者GAETANO SIMONELLI

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 16:17:42