1. 蓝牙广播基础与开发环境搭建蓝牙广播是Android蓝牙开发中最基础也最重要的功能之一。简单来说它就像是蓝牙世界的广播电台设备通过发送广播来告知周围自己的状态变化。想象一下你走进一个会议室所有人的手机蓝牙都在不断喊话有的在说我正在寻找设备有的在说我已经连接上了耳机这就是蓝牙广播的实际场景。要在Android中接收这些广播首先需要确保开发环境配置正确。我建议使用Android Studio 2023.3.1或更高版本搭配API Level 31以上的编译环境。在build.gradle中记得添加以下权限uses-permission android:nameandroid.permission.BLUETOOTH/ uses-permission android:nameandroid.permission.BLUETOOTH_ADMIN/ uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION/ uses-permission android:nameandroid.permission.BLUETOOTH_SCAN android:usesPermissionFlagsneverForLocation/ uses-permission android:nameandroid.permission.BLUETOOTH_CONNECT/这里有个坑我踩过从Android 12开始BLUETOOTH_SCAN权限需要显式声明neverForLocation标志否则会被系统认为你在尝试获取位置信息。另外如果你的应用要支持Android 10及以下版本还需要在代码中动态请求ACCESS_FINE_LOCATION权限。2. 蓝牙状态变化的监听与处理2.1 蓝牙开关状态监听蓝牙适配器的开关状态变化是最基础的广播事件对应BluetoothAdapter.ACTION_STATE_CHANGED。这个广播会告诉你蓝牙是正在打开、已经打开、正在关闭还是已经关闭。在实际项目中我经常用这个广播来同步UI状态。注册广播接收器的典型代码如下private final BroadcastReceiver bluetoothStateReceiver new BroadcastReceiver() { Override public void onReceive(Context context, Intent intent) { final String action intent.getAction(); if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { int state intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); switch (state) { case BluetoothAdapter.STATE_OFF: Log.d(TAG, 蓝牙已关闭); break; case BluetoothAdapter.STATE_TURNING_ON: Log.d(TAG, 蓝牙正在开启); break; case BluetoothAdapter.STATE_ON: Log.d(TAG, 蓝牙已开启); break; case BluetoothAdapter.STATE_TURNING_OFF: Log.d(TAG, 蓝牙正在关闭); break; } } } };2.2 蓝牙扫描模式监听BluetoothAdapter.ACTION_SCAN_MODE_CHANGED广播可以告诉你设备当前是否可被发现。这个功能在需要设备间相互发现的场景特别有用比如文件传输应用。我做过一个智能家居项目就是通过这个广播来判断设备是否处于可被发现状态然后提示用户操作。3. 蓝牙设备发现与交互3.1 设备发现广播处理当蓝牙适配器发现新设备时会发送BluetoothDevice.ACTION_FOUND广播。这个广播携带了丰富的设备信息包括设备对象、名称、信号强度(RSSI)等。这里有个性能优化点在高密度蓝牙环境(如展会现场)可能会频繁收到此广播建议做去重处理。private final BroadcastReceiver deviceFoundReceiver 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); String deviceName intent.getStringExtra(BluetoothDevice.EXTRA_NAME); short rssi intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE); // 设备去重逻辑 if (!discoveredDevices.contains(device.getAddress())) { discoveredDevices.add(device.getAddress()); Log.d(TAG, 发现设备: deviceName RSSI: rssi); } } } };3.2 设备配对状态处理BluetoothDevice.ACTION_BOND_STATE_CHANGED广播会通知设备配对状态的变化。在实际开发中我发现很多开发者会忽略BOND_BONDING这个中间状态直接等待BOND_BONDED这会导致状态处理不准确。正确的做法是if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { BluetoothDevice device intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int state intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR); int prevState intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR); if (state BluetoothDevice.BOND_BONDING) { // 正在配对中 showPairingProgress(device); } else if (state BluetoothDevice.BOND_BONDED) { // 配对成功 onPairingSuccess(device); } else if (state BluetoothDevice.BOND_NONE prevState BluetoothDevice.BOND_BONDING) { // 配对失败 onPairingFailed(device); } }4. 蓝牙连接与数据传输4.1 ACL连接状态监控ACL(异步无连接链路)是蓝牙数据传输的基础。BluetoothDevice.ACTION_ACL_CONNECTED和ACTION_ACL_DISCONNECTED这两个广播可以监控底层连接状态。在我的项目中我通常会用这两个广播来维护一个设备连接状态表。// 连接建立 if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) { BluetoothDevice device intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); connectedDevices.add(device); Log.d(TAG, device.getName() 已连接); } // 连接断开 if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) { BluetoothDevice device intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); connectedDevices.remove(device); Log.d(TAG, device.getName() 已断开); }4.2 音频连接状态处理对于音频类应用需要特别关注BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED广播。A2DP(高级音频分发配置文件)是蓝牙音频传输的标准协议。我曾经开发过一个音频路由应用就是通过这个广播来检测耳机连接状态实现自动切换音频输出设备。if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)) { BluetoothDevice device intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int state intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); if (state BluetoothProfile.STATE_CONNECTED) { // 音频设备已连接 switchAudioOutputToBluetooth(device); } else if (state BluetoothProfile.STATE_DISCONNECTED) { // 音频设备已断开 switchAudioOutputToSpeaker(); } }4.3 服务发现与UUID获取BluetoothDevice.ACTION_UUID广播可以获取设备支持的服务UUID。这个功能在需要特定服务的应用中非常关键。比如我做过一个医疗设备项目就是通过这个广播来确认设备是否支持特定的健康服务协议。if (BluetoothDevice.ACTION_UUID.equals(action)) { BluetoothDevice device intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); Parcelable[] uuidExtra intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID); if (uuidExtra ! null) { ListParcelUuid uuids Arrays.asList(Arrays.copyOf(uuidExtra, uuidExtra.length, ParcelUuid[].class)); if (uuids.contains(MY_CUSTOM_SERVICE_UUID)) { // 设备支持我们的自定义服务 connectToDevice(device); } } }在实际开发中蓝牙广播的处理需要注意几个关键点首先广播接收器要在合适的生命周期注册和注销通常在Activity的onResume和onPause中处理其次高版本Android对后台广播接收有限制需要合理使用前台服务最后处理广播时要考虑线程问题UI更新必须回到主线程。
Android 蓝牙广播实战:从状态监测到设备交互
1. 蓝牙广播基础与开发环境搭建蓝牙广播是Android蓝牙开发中最基础也最重要的功能之一。简单来说它就像是蓝牙世界的广播电台设备通过发送广播来告知周围自己的状态变化。想象一下你走进一个会议室所有人的手机蓝牙都在不断喊话有的在说我正在寻找设备有的在说我已经连接上了耳机这就是蓝牙广播的实际场景。要在Android中接收这些广播首先需要确保开发环境配置正确。我建议使用Android Studio 2023.3.1或更高版本搭配API Level 31以上的编译环境。在build.gradle中记得添加以下权限uses-permission android:nameandroid.permission.BLUETOOTH/ uses-permission android:nameandroid.permission.BLUETOOTH_ADMIN/ uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION/ uses-permission android:nameandroid.permission.BLUETOOTH_SCAN android:usesPermissionFlagsneverForLocation/ uses-permission android:nameandroid.permission.BLUETOOTH_CONNECT/这里有个坑我踩过从Android 12开始BLUETOOTH_SCAN权限需要显式声明neverForLocation标志否则会被系统认为你在尝试获取位置信息。另外如果你的应用要支持Android 10及以下版本还需要在代码中动态请求ACCESS_FINE_LOCATION权限。2. 蓝牙状态变化的监听与处理2.1 蓝牙开关状态监听蓝牙适配器的开关状态变化是最基础的广播事件对应BluetoothAdapter.ACTION_STATE_CHANGED。这个广播会告诉你蓝牙是正在打开、已经打开、正在关闭还是已经关闭。在实际项目中我经常用这个广播来同步UI状态。注册广播接收器的典型代码如下private final BroadcastReceiver bluetoothStateReceiver new BroadcastReceiver() { Override public void onReceive(Context context, Intent intent) { final String action intent.getAction(); if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { int state intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); switch (state) { case BluetoothAdapter.STATE_OFF: Log.d(TAG, 蓝牙已关闭); break; case BluetoothAdapter.STATE_TURNING_ON: Log.d(TAG, 蓝牙正在开启); break; case BluetoothAdapter.STATE_ON: Log.d(TAG, 蓝牙已开启); break; case BluetoothAdapter.STATE_TURNING_OFF: Log.d(TAG, 蓝牙正在关闭); break; } } } };2.2 蓝牙扫描模式监听BluetoothAdapter.ACTION_SCAN_MODE_CHANGED广播可以告诉你设备当前是否可被发现。这个功能在需要设备间相互发现的场景特别有用比如文件传输应用。我做过一个智能家居项目就是通过这个广播来判断设备是否处于可被发现状态然后提示用户操作。3. 蓝牙设备发现与交互3.1 设备发现广播处理当蓝牙适配器发现新设备时会发送BluetoothDevice.ACTION_FOUND广播。这个广播携带了丰富的设备信息包括设备对象、名称、信号强度(RSSI)等。这里有个性能优化点在高密度蓝牙环境(如展会现场)可能会频繁收到此广播建议做去重处理。private final BroadcastReceiver deviceFoundReceiver 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); String deviceName intent.getStringExtra(BluetoothDevice.EXTRA_NAME); short rssi intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE); // 设备去重逻辑 if (!discoveredDevices.contains(device.getAddress())) { discoveredDevices.add(device.getAddress()); Log.d(TAG, 发现设备: deviceName RSSI: rssi); } } } };3.2 设备配对状态处理BluetoothDevice.ACTION_BOND_STATE_CHANGED广播会通知设备配对状态的变化。在实际开发中我发现很多开发者会忽略BOND_BONDING这个中间状态直接等待BOND_BONDED这会导致状态处理不准确。正确的做法是if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { BluetoothDevice device intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int state intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR); int prevState intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR); if (state BluetoothDevice.BOND_BONDING) { // 正在配对中 showPairingProgress(device); } else if (state BluetoothDevice.BOND_BONDED) { // 配对成功 onPairingSuccess(device); } else if (state BluetoothDevice.BOND_NONE prevState BluetoothDevice.BOND_BONDING) { // 配对失败 onPairingFailed(device); } }4. 蓝牙连接与数据传输4.1 ACL连接状态监控ACL(异步无连接链路)是蓝牙数据传输的基础。BluetoothDevice.ACTION_ACL_CONNECTED和ACTION_ACL_DISCONNECTED这两个广播可以监控底层连接状态。在我的项目中我通常会用这两个广播来维护一个设备连接状态表。// 连接建立 if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) { BluetoothDevice device intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); connectedDevices.add(device); Log.d(TAG, device.getName() 已连接); } // 连接断开 if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) { BluetoothDevice device intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); connectedDevices.remove(device); Log.d(TAG, device.getName() 已断开); }4.2 音频连接状态处理对于音频类应用需要特别关注BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED广播。A2DP(高级音频分发配置文件)是蓝牙音频传输的标准协议。我曾经开发过一个音频路由应用就是通过这个广播来检测耳机连接状态实现自动切换音频输出设备。if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)) { BluetoothDevice device intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int state intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); if (state BluetoothProfile.STATE_CONNECTED) { // 音频设备已连接 switchAudioOutputToBluetooth(device); } else if (state BluetoothProfile.STATE_DISCONNECTED) { // 音频设备已断开 switchAudioOutputToSpeaker(); } }4.3 服务发现与UUID获取BluetoothDevice.ACTION_UUID广播可以获取设备支持的服务UUID。这个功能在需要特定服务的应用中非常关键。比如我做过一个医疗设备项目就是通过这个广播来确认设备是否支持特定的健康服务协议。if (BluetoothDevice.ACTION_UUID.equals(action)) { BluetoothDevice device intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); Parcelable[] uuidExtra intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID); if (uuidExtra ! null) { ListParcelUuid uuids Arrays.asList(Arrays.copyOf(uuidExtra, uuidExtra.length, ParcelUuid[].class)); if (uuids.contains(MY_CUSTOM_SERVICE_UUID)) { // 设备支持我们的自定义服务 connectToDevice(device); } } }在实际开发中蓝牙广播的处理需要注意几个关键点首先广播接收器要在合适的生命周期注册和注销通常在Activity的onResume和onPause中处理其次高版本Android对后台广播接收有限制需要合理使用前台服务最后处理广播时要考虑线程问题UI更新必须回到主线程。