蓝牙适配器初始化与设备发现流程
本章进入实际编码阶段,系统讲解 BluetoothAdapter 的获取、启用、设备发现启动以及广播接收器的注册与撤销。掌握这些流程是构建蓝牙通信应用的基础,后续所有配对与数据传输操作都依赖本环节的正确实现。
3.1 核心原理
Android 蓝牙框架采用分层架构:上层的 BluetoothAdapter 代表本地蓝牙设备(即手机本身的蓝牙硬件),通过它才能控制蓝牙模块的开关、发起发现、获取绑定设备列表等。设备发现(Discovery)本质是让本机发送查询请求,并监听周围蓝牙设备发出的响应广播。这些广播由系统通过 Intent 发送,应用需要通过 BroadcastReceiver 接收并解析。
| 组件 | 职责 |
|---|---|
| BluetoothAdapter | 单例模式封装本地蓝牙硬件操作,提供 enable()、disable()、startDiscovery()、getBondedDevices() 等核心方法。 |
| IntentFilter | 声明应用关注的蓝牙广播类型,如 BluetoothDevice.ACTION_FOUND、BluetoothAdapter.ACTION_DISCOVERY_STARTED。 |
| BroadcastReceiver | 接收并处理广播,从中提取 BluetoothDevice 对象,获取设备名称、MAC 地址、RSSI 信号强度。 |
3.2 实现流程
标准设备发现流程分为 6 个步骤,每一步都必须检查状态并处理异常。
- 获取蓝牙适配器:调用
BluetoothAdapter.getDefaultAdapter(),若返回null表示设备不支持蓝牙,应提示用户。 - 检查并请求启用:若适配器未启用,调用
startActivityForResult()弹出系统启用对话框,或直接调用adapter.enable()(需要BLUETOOTH_ADMIN权限)。 - 注册广播接收器:创建
IntentFilter添加ACTION_FOUND、ACTION_DISCOVERY_FINISHED等 Action,使用registerReceiver()注册。 - 取消正在进行的发现:调用
adapter.cancelDiscovery()避免重复发现影响性能。 - 启动发现:调用
adapter.startDiscovery(),异步执行,发现过程约持续 12 秒。 - 在接收器中处理结果:重写
onReceive()方法,将发现的设备存入列表或直接存储到 SharedPreferences。
注意: Android 10(API 29)及以上要求
ACCESS_FINE_LOCATION 权限才能扫描到蓝牙设备;Android 12 新增 BLUETOOTH_SCAN、BLUETOOTH_ADVERTISE、BLUETOOTH_CONNECT 等运行时权限,需适配。
3.3 核心 API 与代码示例
以下代码片段演示完整的初始化与发现流程,包含运行时权限检查(以 API 31 为例)。
// 1. 获取适配器
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
// 设备不支持蓝牙
return;
}
// 2. 启用蓝牙(方式一:弹出系统对话框)
if (!bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
// 在 onActivityResult 中继续后续步骤
}
// 3. 创建广播接收器
private final BroadcastReceiver discoveryReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, (short) 0);
String deviceName = device.getName();
String deviceAddress = device.getAddress();
// 存储到列表或 SharedPreferences
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
// 发现结束,可更新 UI
}
}
};
// 4. 注册接收器
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(discoveryReceiver, filter);
// 5. 取消之前的发现并启动新发现
bluetoothAdapter.cancelDiscovery();
bluetoothAdapter.startDiscovery();
重要:必须在
onDestroy() 或不再需要时调用 unregisterReceiver(discoveryReceiver),否则会引发内存泄漏。同时,startDiscovery() 是耗电操作,建议在 Activity 可见时调用,并在离开界面时及时取消。
3.4 调试与常见错误
| 错误场景 | 可能原因 | 解决方案 |
|---|---|---|
adapter 为 null |
设备无蓝牙硬件或模拟器未配置 | 检查设备兼容性,真机调试 |
| 无法发现任何设备 | 权限不足 / 蓝牙未启用 / 位置未开 | 确认已授予 BLUETOOTH、BLUETOOTH_ADMIN、ACCESS_FINE_LOCATION;在设置中开启位置服务 |
| 广播接收器未收到 ACTION_FOUND | 未正确注册 / IntentFilter 缺少 Action / 注册在非 Activity Context | 使用 Activity 实例注册,或确保 Application Context 已正确处理 |
| 发现中途停止 | 多次调用 startDiscovery() 导致冲突 |
始终先调用 cancelDiscovery() 再启动 |
| Android 12+ 无设备出现 | 未请求运行时蓝牙权限 | 在代码中请求 BLUETOOTH_SCAN、BLUETOOTH_CONNECT,且目标 SDK 需适配 |
3.5 性能与稳定性优化
- 避免频繁发现:一次发现过程耗电约 20~30 mA,连续调用会大幅缩短续航;建议在用户主动点击“扫描”时才启动,并设置超时自动停止(如 15 秒后调用
cancelDiscovery())。 - 及时注销广播:在
onPause()或onStop()中调用unregisterReceiver,在onResume()中重新注册,避免 Activity 不可见时仍占用资源。 - 缓存发现结果:对发现的设备按 MAC 地址去重,避免重复添加相同设备;可使用
HashMap<String, BluetoothDevice>临时存储,发现结束后一次性写入 SharedPreferences。 - 检查适配器状态:每次操作前调用
isEnabled()确认蓝牙仍在开启状态,防止用户在使用过程中手动关闭蓝牙导致异常。
3.6 设备发现流程示意图
3.7 本章小结
蓝牙适配器初始化与设备发现是蓝牙通信的入口环节。开发者需牢记以下要点:
- 必须检查
BluetoothAdapter是否为null; - 启用蓝牙前先检查
isEnabled(),优先使用系统对话框而非强制enable(); - 广播接收器的注册与注销必须成对出现;
- Android 11+ 对蓝牙扫描权限要求严格,务必处理运行时权限回调;
- 使用
getBondedDevices()可获取已配对设备作为快速连接缓存。