Android BLE 按正确 UUID 查找不到 micro:bit 服务,如何排查?
结论:Android 当前发现的服务列表中没有目标 UUID
日志显示,本次 Android 服务发现结果中没有 e95d0753-251d-470a-a062-fa1922dfa9a8。gatt.getService() 返回 null,与这份服务列表一致。
UUID 字符串与打印出的高、低 64 位相互对应,没有看到 UUID 解析错误的证据。调整 UUID 大小写或交换高低位,无法解决当前的服务列表差异。
现有材料还不足以解释电脑和手机为何看到了不同的服务,也不足以将原因归结为某项权限、Android 14、OnePlus 系统或 micro
固件问题。先区分服务发现成功和目标服务存在
BluetoothGatt.GATT_SUCCESS 表示本次服务发现操作成功完成,但不保证目标服务存在。
设备建立连接并触发 onConnectionStateChange(),也不能证明应用已经具备读取目标 characteristic 的条件。还需要依次确认:
- 连接已成功建立。
- 服务发现成功完成。
- 返回的服务列表包含目标 service UUID。
- 该服务包含目标 characteristic UUID。
当前日志显示第 3 步尚未满足,应先查明目标服务为何缺失,再检查读取和 UDP 转发逻辑。
按相同条件比较电脑和手机
电脑曾发现 accelerometer service,只能说明它在那次连接中看到了该服务。建议按以下顺序重新对比:
- 断开电脑上的 bleak 连接,并停止可能自动重连的程序。
- 确认手机连接的是同一块 micro,同时记录设备地址,避免仅凭设备名称判断。
- 保持 micro 上运行的程序、固件和运行模式一致,分别重新连接电脑与手机。
- 两端都在连接后完成 GATT 服务发现,再输出完整的 service 和 characteristic 列表。
比较时应使用连接后发现的 GATT 服务列表。广播数据中的服务 UUID 列表未必完整,不能直接替代服务发现结果。
如果相同条件下电脑仍能发现目标服务,而手机始终缺少它,就有了可重复的对照结果,可以继续调查客户端状态、配对状态和外设行为。
检查外设配置和手机保存的状态
以下几种可能性都需要验证,现有日志尚不能确认其中任何一种。
- 外设程序或运行模式不同:核对当前 micro 程序是否启用了 accelerometer service,以及两次测试期间是否重新烧录或切换过模式。
- 配对或安全状态不同:如果当前固件对服务访问有相关要求,应按照该固件的配置检查两端状态,不能仅凭系统权限已授权就排除这类差异。
- 手机保留了旧的服务信息:如果曾给同一块 micro 烧录过不同的 BLE 程序,可以把旧状态作为排查方向,但不能仅凭服务缺失就认定是缓存问题。
可以先结束当前连接,调用旧 BluetoothGatt 实例的 close() 释放资源,再创建新连接并重新发现服务。如果此前已经配对,还可以在明确会清除配对关系的前提下,取消配对、重启外设并重新配对,观察服务列表是否改变。
这些操作有助于缩小排查范围,但不能保证恢复目标服务。也不建议把反射调用隐藏的 refresh() 方法作为正式修复方案。
分别检查 service 和 characteristic
原代码只检查了 service 是否存在。即使找到了 service,getCharacteristic() 仍可能返回 null。此时直接设置 BleStatus.Connected,会让后续读取流程误以为已经准备完成。
可以沿用现有变量和状态类型,将两个检查分开:
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
if (status != BluetoothGatt.GATT_SUCCESS) {
targetCharacteristic = null
_status.value = BleStatus.Error("Service discovery failed: $status")
return
}
Log.i("mb2o", "device = ${gatt.device.address}")
gatt.services.forEach { service ->
Log.i("mb2o", "service = ${service.uuid}")
service.characteristics.forEach { characteristic ->
Log.i("mb2o", " characteristic = ${characteristic.uuid}")
}
}
val serviceUuid = UUID.fromString(targetServiceUuid)
val characteristicUuid = UUID.fromString(targetCharacteristicUuid)
val service = gatt.getService(serviceUuid)
if (service == null) {
targetCharacteristic = null
_status.value = BleStatus.Error("Service not found: $serviceUuid")
return
}
val characteristic = service.getCharacteristic(characteristicUuid)
if (characteristic == null) {
targetCharacteristic = null
_status.value = BleStatus.Error(
"Characteristic not found: $characteristicUuid"
)
return
}
targetCharacteristic = characteristic
_status.value = BleStatus.Connected
}
这段代码用于完善诊断和就绪判断,不会让缺失的服务自动出现。应用也需要先取得调用相关 API 所需的权限。
如果应用存在断线重连,还要确认处理的是当前连接对应的 BluetoothGatt 回调,避免将旧连接的结果混入日志。
event service 能否替代 accelerometer service?
不能因为 UUID 接近就直接替换。日志中的 e95d93af-251d-470a-a062-fa1922dfa9a8 对应 event service,它与 accelerometer service 是不同的服务。
event service 能否满足需求,取决于 micro
程序提供的事件及其数据含义。原始需求是反复读取指定 characteristic 并通过 UDP 转发;现有材料没有证明 event service 能提供所需的加速度数据,因此不能把更换 UUID 当作等效修复。找到目标 characteristic 后,还应确认它支持所需的读取方式。Android GATT 读取是异步操作,轮询时应等待读取回调完成,再安排下一次读取,避免在线程循环中连续提交请求。
备注:内容仅供参考。
