上一篇
安卓开发之蓝牙连接与数据发送
- 行业动态
- 2025-04-21
- 4
权限配置
在AndroidManifest.xml中声明必要权限:
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
注意:Android 6.0+需动态申请位置权限(定位权限是蓝牙扫描的依赖条件)。
蓝牙适配器初始化
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null) { // 设备不支持蓝牙 Toast.makeText(this, "设备不支持蓝牙", Toast.LENGTH_SHORT).show(); finish(); }
开启蓝牙
if (!bluetoothAdapter.isEnabled()) { Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent, REQUEST_ENABLE_BT); }
注意:需处理onActivityResult
回调,判断用户是否同意开启。
扫描蓝牙设备
注册广播接收器
private final BroadcastReceiver receiver = 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); // 处理发现的设备(如添加到列表) } } };
开始扫描
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(receiver, filter); bluetoothAdapter.startDiscovery();
停止扫描
bluetoothAdapter.cancelDiscovery(); unregisterReceiver(receiver);
设备配对与连接
配对设备
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); device.createBond(); // 触发配对流程
注意:需监听BluetoothDevice.ACTION_BOND_STATE_CHANGED
广播,确认配对成功后再连接。
创建蓝牙连接
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")); try { socket.connect(); // 阻塞式调用,需在子线程执行 } catch (IOException e) { e.printStackTrace(); }
数据发送与接收
发送数据
OutputStream outputStream = socket.getOutputStream(); String data = "Hello Bluetooth"; outputStream.write(data.getBytes(StandardCharsets.UTF_8)); // 需处理异常
接收数据
InputStream inputStream = socket.getInputStream(); byte[] buffer = new byte[1024]; int len; while ((len = inputStream.read(buffer)) != -1) { String received = new String(buffer, 0, len, StandardCharsets.UTF_8); // 处理接收到的数据 }
注意:接收数据需在子线程执行,避免阻塞主线程。
线程管理与资源释放
读取线程示例
new Thread(() -> { InputStream inputStream = socket.getInputStream(); byte[] buffer = new byte[1024]; try { while ((inputStream.read(buffer)) != -1) { // 通过Handler或LiveData更新UI } } catch (IOException e) { e.printStackTrace(); } }).start();
断开连接
try { socket.close(); // 关闭输入输出流和Socket } catch (IOException e) { e.printStackTrace(); }
常见问题与解决方案
问题 | 解决方案 |
---|---|
蓝牙权限被拒导致功能不可用 | 动态申请位置权限(ACCESS_FINE_LOCATION ),并解释权限用途 |
设备扫描不到目标设备 | 检查设备可见性(需目标设备开启可见模式) |
连接失败或数据传输中断 | 确保设备已配对,检查UUID是否匹配,处理线程异常 |
接收数据粘包或乱码 | 定义数据协议(如添加消息头或固定长度字段) |
相关问题与解答
问题1:如何优化蓝牙连接的稳定性?
解答:
- 重试机制:连接失败时延迟重试(如3秒后重试2次)。
- 心跳包:定期发送轻量级数据(如
ping
)检测连接状态。 - 异常处理:捕获
IOException
并尝试重新连接。 - 线程隔离:将读写操作放在独立线程,避免阻塞主线程。
问题2:如何实现蓝牙数据的分包发送?
解答:
- 拆分数据:将大数据分割为固定大小的数据包(如每包20字节)。
- 添加标识:每个数据包添加序号或校验和(如CRC16)。
- 接收端重组:按序号拼接数据包,并验证完整性。
- 示例代码:
// 发送端分包 byte[] fullData = "Large Data".getBytes(); int packetSize = 20; for (int i = 0; i < fullData.length; i += packetSize) { int len = Math.min(packetSize, fullData.length i); outputStream.write(fullData, i, len); // 分批次发送 }