socket.connect();
// 连接成功,可以开始进行数据交换
} catch (IOException e) {
// 连接失败
e.printStackTrace();
}
3. 数据交换

连接成功后,你可以通过BluetoothSocket的输入输出流来进行数据的读写。

try {
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
// 从InputStream读取数据
byte[] buffer = new byte[1024];
int bytes;
bytes = inputStream.read(buffer);
String receivedData = new String(buffer, 0, bytes);
// 向OutputStream写入数据
String dataToSend = “Hello, Bluetooth!”;
outputStream.write(dataToSend.getBytes());
} catch (IOException e) {
// 处理异常
}
4. 断开连接

完成数据交换后,应该关闭BluetoothSocket来释放资源

try {
socket.close();
} catch (IOException e) {
// 处理异常
}

注意事项

- 连接过程可能会花费一些时间,建议在一个单独的线程中进行连接操作,以避免阻塞UI线程。
- 确保你的UUID与远程设备提供的服务UUID匹配。
- 在连接过程中和之后,要妥善处理异常。

疑问:

我听同事说发送数据还有片段发送,是怎么样的

解答:

在蓝牙通信中,由于蓝牙协议本身的限制以及不同设备的性能差异,发送大量数据时直接发送整个数据包可能会导致数据传输失败或者效率低下。

因此,将大数据分割成小片段(fragments)进行发送是一种常见的做法,这就是所谓的“片段发送”或“数据分片”。

1.数据分片的基本思想

数据分片的基本思想,是将大的数据包分割成多个较小的数据片段,然后逐个片段地发送这些数据。接收方收到所有片段后,再将它们重新组合成原始的数据包。

2.如何实现数据分片?

实现数据分片的一个简单方法是:

  • (1)确定片段大小:
  • 首先,你需要确定每个数据片段的大小。
  • 这个大小需要根据你的应用需求和蓝牙硬件的能力来决定。一个常见的做法是使用512字节作为每个片段的大小。
  • (2)分割数据
  • 将你的数据分割成多个片段。
  • 如果数据大小不是片段大小的整数倍,最后一个片段可能会更小。
  • (3)发送数据片段:
  • 逐个发送每个数据片段。
  • 在每个片段发送之后,可能需要等待接收方的确认,再发送下一个片段。
  • (4)接收方重组数据:
  • 接收方需要按照发送顺序接收并存储每个数据片段。
  • 一旦所有片段都被接收,接收方就可以将它们组合成原始的数据包。
示例代码

以下是一个简化的示例,展示了如何将一个大的字节数组分割成多个片段并发送:

OutputStream outputStream = bluetoothSocket.getOutputStream();
byte[] largeData = …; // 假设这是要发送的大数据
int chunkSize = 512; // 片段大小
int offset = 0;
while (offset < largeData.length) {
// 计算剩余数据大小
int remaining = largeData.length - offset;
int byteCount = Math.min(remaining, chunkSize); // 这次发送的字节数
// 发送数据片段
outputStream.write(largeData, offset, byteCount);
outputStream.flush(); // 确保数据被发送
offset += byteCount; // 更新偏移量
// 可选:等待接收方的确认
}
// 数据发送完毕
注意事项

- 数据分片会增加通信的复杂度,特别是在需要确保数据完整性和顺序的情况下。
- 接收方需要有相应的逻辑来处理接收到的数据片段,包括片段的重组和可能的错误处理。
- 在实际应用中,可能还需要考虑如何处理数据传输中的错误,例如重传丢失的片段。

通过数据分片,你可以更可靠地通过蓝牙发送大量数据,同时还可以提高数据传输的效率。

示例代码

获取蓝牙设备电量

public void getBluetoothDeviceBattery() {
 BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
 //获取BluetoothAdapter的Class对象
 Class bluetoothAdapterClass = BluetoothAdapter.class;
 try {
 //反射获取蓝牙连接状态的方法
 Method method = bluetoothAdapterClass.getDeclaredMethod(“getConnectionState”, (Class[]) null);
 //打开使用这个方法的权限
 method.setAccessible(true);
 int state = (int) method.invoke(btAdapter, (Object[]) null);if (state == BluetoothAdapter.STATE_CONNECTED) {
 //获取在系统蓝牙的配对列表中的设备–!已连接设备包含在其中
 if (ActivityCompat.checkSelfPermission(this,
 android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
 Log.e(TAG, “没有权限”);
 return;
 }
 Set devices = btAdapter.getBondedDevices();
 for (BluetoothDevice device : devices) {
 Method batteryMethod = BluetoothDevice.class.getDeclaredMethod(“getBatteryLevel”, (Class[]) null);
 batteryMethod.setAccessible(true);
 Method isConnectedMethod = BluetoothDevice.class.getDeclaredMethod(“isConnected”, (Class[]) null);
 isConnectedMethod.setAccessible(true);
 boolean isConnected = (boolean) isConnectedMethod.invoke(device, (Object[]) null);
 int level = (int) batteryMethod.invoke(device, (Object[]) null);
 if (device != null && level > 0 && isConnected) {
 String deviceName = device .getName();
 // LogUtils.d(deviceName + " 电量: " + level);
 Log.e(TAG, deviceName + " 电量: " + level);
 }
 }
 } else {
 // ToastUtils.showLong(“No Connected Bluetooth Devices Found”);
 Log.e(TAG, “No Connected Bluetooth Devices Found”);
 }
 } catch (Exception e) {
 Log.e(TAG, “Exception”+e.toString());
 e.printStackTrace();
 }}

总之,这就是如何连接到蓝牙设备并进行数据交换的基本步骤。理解这些步骤对于开发能够与其他蓝牙设备交互的应用是非常重要的。

当你准备好了,请告诉我,以便我们继续下一节。

第五节:管理蓝牙连接

在这一节中,我们将讨论如何管理蓝牙连接,包括:数据的接收处理、连接的维护和断开连接的正确方式。

1. 接收数据

在前一节中,我们讨论了如何发送数据。接收数据同样重要,通常涉及到从BluetoothSocket的输入流中读取数据。

由于数据接收是阻塞操作,推荐在单独的线程中进行读取操作,以避免阻塞UI线程。

class ConnectedThread extends Thread {
private final BluetoothSocket socket;
private final InputStream inputStream;
private final OutputStream outputStream;
public ConnectedThread(BluetoothSocket socket) {
this.socket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
inputStream = tmpIn;
outputStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024];  // 缓冲区
int bytes; // 读取的字节数
while (true) {
try {
// 从InputStream读取数据
bytes = inputStream.read(buffer);
String receivedData = new String(buffer, 0, bytes);
// 处理接收到的数据
} catch (IOException e) {
// 连接丢失时的处理
break;
}
}
}
// 调用此方法从外部发送数据
public void write(byte[] bytes) {
try {
outputStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
// 调用此方法关闭连接
public void cancel() {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. 维护连接

维护蓝牙连接的关键,在于处理各种可能导致连接中断的情况。

例如,设备的距离过远、干扰或设备关闭。

ConnectedThreadrun方法中,如果**inputStream.read(buffer)**抛出IOException,通常意味着连接已经丢失,此时应当关闭socket并尝试重新连接或更新UI。

3. 断开连接

当不再需要蓝牙连接时,正确的断开连接是很重要的。这包括关闭BluetoothSocket,以及与之关联的输入输出流。

ConnectedThread类中,我们提供了一个cancel方法来做这件事。调用cancel可以安全地关闭socket并终止线程。

public void cancel() {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
4. 重连策略

在实际应用中,可能需要实现自动重连的逻辑。这通常涉及到在连接丢失后等待一段时间,然后尝试重新连接到设备。

重连策略的实现会根据具体的应用需求而有所不同,但基本思路是在捕获到连接丢失的异常后,延迟一段时间后再次尝试连接。

这就是关于管理蓝牙连接的基本知识。通过正确地管理连接,你的应用可以更加稳定,为用户提供更好的体验。

当你准备好了,请告诉我,以便我们继续下一节。

第六节:蓝牙服务和特征

在这一节中,我们将探讨使用蓝牙低功耗(BLE)时的高级概念,特别是蓝牙服务特征的概念。这些概念对于理解和开发BLE应用至关重要。

1. BLE简介

蓝牙低功耗(BLE),也称为蓝牙4.0 或 蓝牙Smart,是蓝牙技术的一个版本,专为低功耗设备设计,适用于需要长期运行在电池供电下的设备。与经典蓝牙相比,BLE在保持通信能力的同时,大大降低了能耗。

2. 服务和特征

在BLE中,数据交换是通过服务(Services)和特征(Characteristics)的概念来组织的。

- 服务(Service):

服务是一组功能相关的特征的集合。每个服务都由一个唯一的UUID来标识。

- 特征(Characteristic):

特征表示服务中的一个数据点,例如一个传感器的值或者一个配置参数。特征包含一个值以及0到多个描述符(Descriptors),用于描述特征的属性。特征也由UUID标识。

3. 发现服务和特征

当你的设备与一个BLE设备建立连接后,下一步通常是发现远程设备提供的服务和特征。这可以通过调用BluetoothGatt对象的**discoverServices()**方法来完成。

服务发现完成后,你可以通过调用**getServices()**方法来获取服务列表,然后遍历这些服务以查找特定的服务和特征。

@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
for (BluetoothGattService service : gatt.getServices()) {
UUID serviceUUID = service.getUuid();
// 检查是否是我们感兴趣的服务UUID
for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
UUID characteristicUUID = characteristic.getUuid();
// 检查是否是我们感兴趣的特征UUID
}
}
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
4. 读写特征

一旦找到了感兴趣的特征,你可以读取特征的值或向其写入值。

-读取****特征值:

调用BluetoothGatt的**readCharacteristic()**方法。

-写入特征值:

首先设置特征的值,然后调用BluetoothGatt的**writeCharacteristic()**方法。

// 读取特征值
gatt.readCharacteristic(characteristic);
// 写入特征值
characteristic.setValue(value);
gatt.writeCharacteristic(characteristic);
5. 订阅特征通知

对于一些特征,你可能希望当特征的值发生变化时得到通知。

这可以通过对特征启用通知来实现 BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE

这通常涉及到写入特征的一个相关描述符。

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);

这就是BLE服务和特征的基本概念。

通过理解和使用这些概念,你可以开发出能够与BLE设备进行复杂交互的应用。

当你准备好了,请告诉我,以便我们继续下一节。

第七节:蓝牙安全性和最佳实践

在这一节中,我们将讨论蓝牙通信的安全性问题以及开发蓝牙应用时的一些最佳实践。

1. 蓝牙安全性概述

蓝牙技术包含多种安全特性,旨在保护通信不被未授权访问。这些安全特性包括配对过程中的认证和加密。然而,蓝牙设备和通信仍可能面临一些安全威胁,如中间人攻击(MITM)、身份欺骗等。因此,开发者在使用蓝牙技术时需要采取适当的安全措施。