4. 解析蓝牙设备广播数据并获取设备信息
4.1 原理概述
低功耗蓝牙(BLE)与经典蓝牙在设备发现阶段都会通过系统广播发送 BluetoothDevice.ACTION_FOUND 事件。应用通过注册 BroadcastReceiver 接收该广播,从 Intent 的 EXTRA_DEVICE 中取出 BluetoothDevice 对象,再调用其 API 获取名称和 MAC 地址。RSSI(信号强度)可通过 EXTRA_RSSI 获取,辅助判断距离。
4.2 实现流程
| 步骤 | 操作 | 关键 API / 字段 |
|---|---|---|
| 1 | 注册 Receiver | IntentFilter(BluetoothDevice.ACTION_FOUND) |
| 2 | 重写 onReceive() | intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) |
| 3 | 获取设备名称 | device.name ?: “未知设备” |
| 4 | 获取 MAC 地址 | device.address |
| 5 | 获取 RSSI | intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, 0.toShort()) |
| 6 | 封装数据对象 | 自定义 BluetoothDeviceInfo 数据类 |
4.3 核心代码实现
4.3.1 数据模型
data class BluetoothDeviceInfo(
val name: String,
val address: String,
val rssi: Int
)
4.3.2 BroadcastReceiver 完整实现
class BluetoothFoundReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (action != BluetoothDevice.ACTION_FOUND) return
val device: BluetoothDevice? =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
val rssi = intent.getShortExtra(
BluetoothDevice.EXTRA_RSSI, 0.toShort()
).toInt()
device?.let {
val name = it.name ?: "未知设备"
val address = it.address
val info = BluetoothDeviceInfo(name, address, rssi)
// 回调或存入集合,供 UI 更新
onDeviceFound?.invoke(info)
}
}
// 回调函数,在 Activity / Fragment 中赋值
var onDeviceFound: ((BluetoothDeviceInfo) -> Unit)? = null
}
4.3.3 注册与反注册(推荐在 onResume / onPause 中执行)
private val receiver = BluetoothFoundReceiver()
private val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
// 注册
registerReceiver(receiver, filter)
// 反注册
unregisterReceiver(receiver)
4.4 调试技巧
- 在
onReceive中添加日志,输出设备名称和地址:Log.d(“BT_DEBUG”, “Found: $name, $address”) - 使用
adb shell dumpsys bluetooth_manager查看系统蓝牙状态 - 模拟器不支持蓝牙发现,请在真机上测试
- Android 12+ 需动态请求
BLUETOOTH_SCAN权限,否则广播可能被拦截
4.5 常见错误与解决方案
| 错误现象 | 可能原因 | 解决 |
|---|---|---|
| onReceive 不被触发 | 未注册 Receiver 或 IntentFilter 错误 | 检查 BluetoothDevice.ACTION_FOUND 字符串拼写 |
| device 为 null | Intent 未包含 EXTRA_DEVICE | 检查系统版本限制:Android 12+ 需 BLUETOOTH_SCAN 权限 |
| 应用闪退 | 在主线程做耗时操作 | Receiver 运行在主线程,只做简单封装,通过 Handler / LiveData 更新 UI |
| 获取名称始终为“未知设备” | BLE 设备有时不广播名称 | 使用 scanRecord 解析本地名称,或依赖 GAP 数据 |
4.6 性能与稳定性优化
- 避免阻塞:
onReceive在主线程执行,不要做网络请求、数据库写入或复杂计算;只做数据封装和轻量回调。 - 使用集合去重:相同 MAC 的设备多次出现时,用
HashSet或ConcurrentHashMap过滤重复。 - 及时注销:Activity/Fragment 销毁后未取消注册会导致内存泄漏,务必在
onPause或onDestroy中unregisterReceiver。 - RSSI 平滑:若需要信号强度稳定性,可对多次 RSSI 求滑动平均,避免单次抖动。
4.7 广播数据解析流程
下图展示了从系统蓝牙广播到数据对象封装的完整路径: