BLE技术与Android开发实践指南
BLE技术概述
1. BLE发展历程
BLE(Bluetooth Low Energy)是专为低功耗应用设计的蓝牙技术,起源于蓝牙4.0规范。与传统蓝牙相比,BLE显著降低了能耗,适合功耗敏感的设备。
2. BLE应用场景
BLE技术在健康监测设备、智能家居、物联网等领域广泛应用,因其低功耗特性而备受青睐,能够实现移动设备与低功耗外设间的快速、稳定连接。
3. BLE技术优势
- 快速建立连接,简化配对过程,支持即连即用。
- 低功耗,适合长时间运行的便携式设备。
- 适中的传输距离,一般在010米,最远可达0100米。
4. BLE技术挑战
- 与传统蓝牙不兼容,需专门的支持和驱动。
- 传输速率较低,不适合大数据量传输。
蓝牙GATT协议详解
1. GATT协议概述
GATT(Generic Attribute Profile)基于ATT协议,ATT协议是一种双向的、基于应答的协议。GATT是蓝牙设备间进行数据交换的标准协议之一,用于描述和传输设备之间的数据。
GATT-Based Profile hierarchy
2. GATT协议的核心组件
- 服务(Service):表示某个功能,如心率服务,包含多个特征。
- 特征(Characteristic):服务中的数据单元,包含数据和权限。
- 描述符(Descriptor):描述特征的元数据,如单位、范围等。
- 角色:Server(服务端)和Client(客户端),分别负责提供数据和读取/写入数据。
3. GATT协议的操作流程
- 设备连接:Client与Server建立连接。
- 服务发现:Client查询Server提供的服务。
- 读取特征:Client从Server读取特征值。
- 写入特征:Client向Server写入特征值。
- 启用通知:Client订阅Server的特征通知。
4. GATT协议的常见问题与解决方案
- 检查特征权限,确保客户端具有读取或写入权限。
- 优化数据传输频率,减少通知的延迟和丢失。
- 根据设备性能调整通知频率,避免过载。
Android BLE开发基础
1. BLE概述
BLE是蓝牙4.0规范的一部分,提供低成本、低功耗的无线通信方式。
2. Android BLE API
Android BLE API包括BluetoothAdapter、BluetoothDevice、BluetoothGatt等类,用于扫描设备、连接设备、读取和写入数据。
2.1 BLE连接流程
- 扫描与广播:BLE设备通过广播发送信息,Android设备扫描这些广播以发现附近的BLE设备。
- 连接建立:Android设备向发现的BLE设备发送连接请求。
- 连接参数更新:在连接过程中,可以根据需要更新连接参数。
- 连接断开:当不再需要数据交互时,可以主动断开连接。
2.2 数据交互机制
- GATT服务与特性:服务是一组相关特性的集合,特性是数据的实际表示。
- 读操作:Android设备可以读取BLE设备上特性中的数据。
- 写操作:Android设备可以向BLE设备写入数据。
- 通知与指示:BLE设备可以主动向Android设备发送通知。
2.3 数据安全与隐私
- 加密与认证:保护数据安全,BLE通信可以采用加密和认证机制。
- 隐私保护:BLE设备可以采用隐私保护措施,如随机化广播地址。
- 权限控制:Android设备可以设置权限,限制对BLE设备数据的访问。
- 安全配对:BLE设备间进行配对时,可以设置安全配对流程。
3. Android BLE开发流程
- 权限申请:动态申请位置权限以进行BLE扫描。
- 设备扫描:开启BLE扫描,收集周边设备的广告信息。
- 设备连接:与用户选择的设备建立连接。
- 数据交互:根据设备的服务和特征,读取或写入数据。
3.1 权限申请
- 在Android 6.0及以上版本,需要动态申请位置权限以进行BLE扫描。同时,需要在AndroidManifest.xml中声明ACCESS_COARSE_LOCATION和ACCESS_FINE_LOCATION权限。
- 在Android 12.0及以上版本,引入了 BLUETOOTH_SCAN、BLUETOOTH_ADVERTISE 和 BLUETOOTH_CONNECT 权限,可让您的应用扫描附近的设备,而无需请求位置权限。
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permissionandroid:name="android.permission.BLUETOOTH_PRIVILEGED"
tools:ignore="ProtectedPermissions" />
<!--在Android 12以下 这个权限是必须要的-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--在Android 12以上 可使用以下权限,考虑兼容都加上-->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
3.2 设备扫描
- 通过BluetoothAdapter.startLeScan扫码低功耗蓝牙设备
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
// Device does not support Bluetooth
} else {
bluetoothAdapter.startLeScan(new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
// UI操作,显示扫描到的设备
}
});
}
});
}
- 通过BluetoothLeScanner.startScan扫码低功耗蓝牙设备
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Toast.makeText(this, "蓝牙未开启", Toast.LENGTH_SHORT).show();
return;
}
BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();
if (scanner != null) {
scanner.startScan(new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
BluetoothDevice device = result.getDevice();
// 处理扫描到的设备,如果你需要实时处理每个扫描到的设备,可以使用onScanResult。
Log.i("TAG", "发现设备: " + device.getName() + " - " + device.getAddress());
}
@Override
public void onBatchScanResults(List < ScanResult > results) {
super.onBatchScanResults(results);
for (ScanResult result: results) {
BluetoothDevice device = result.getDevice();
// 处理扫描到的设备,如果你希望减少回调次数,提高处理效率,可以使用onBatchScanResults。
Log.i("TAG", "发现设备: " + device.getName() + " - " + device.getAddress());
}
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.e("TAG", "扫描失败: " + errorCode);
}
});
} else {
Log.e("TAG", "无法获取BluetoothLeScanner");
}
3.3 设备连接
BluetoothGatt gatt = device.connectGatt(context, false, new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// 获取服务和特性,这里具体根据实际设备情况处理
BluetoothGattService linkLossService = mBluetoothGatt.getService(UUID.fromString("EA3A9D7C-9F3A-4BE5-A1B1-7BF0C7E01A5D"));
if (linkLossService != null){
BluetoothGattCharacteristic characteristicNotify = linkLossService.getCharacteristic(UUID.fromString("EA3A9D7C-9F3A-4BE5-A1B2-7BF0C7E01A5D"));
BluetoothGattCharacteristic characteristicWrite = linkLossService.getCharacteristic(UUID.fromString("EA3A9D7C-9F3A-4BE5-A1B3-7BF0C7E01A5D"));
}
}
}
});
3.4 数据交互
- 读取和写入特性
BluetoothGattService service = gatt.getService(serviceUUID);
if (service != null) {
BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUUID);
if (characteristic != null) {
gatt.readCharacteristic(characteristic); // 读取数据
// 写入数据
characteristic.setValue(data);
gatt.writeCharacteristic(characteristic);
}
}
- 设置通知和写入数据
// 启用通知
mBluetoothGatt.setCharacteristicNotification(characteristicNotify, true);
// 设置通知的客户端描述符
BluetoothGattDescriptor descriptor = characteristicNotify.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
// 写入数据
boolean resultSet = characteristicWrite.setValue(data);
if (resultSet) {
boolean resultWrite = mBluetoothGatt.writeCharacteristic(characteristicWrite);
if (!resultWrite) {
// 处理写入失败
}
}
- 蓝牙BluetoothGattCallback中的结果处理
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// 处理读取到的数据
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// 处理写入成功
if (characteristic.getValue() != null && characteristic.getValue().length > 0){
byte[] bytes = characteristic.getValue();
}
} else {
// 处理写入失败
}
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// 处理描述符写入成功
} else {
// 处理描述符写入失败
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (characteristic.getValue() != null && characteristic.getValue().length > 0){
byte[] bytes = characteristic.getValue();
}
}
4. BLE开发注意事项
4.1 开发环境问题
- 环境搭建:确保Android SDK版本与BLE支持版本匹配。
- SDK选择:选择适合BLE开发的SDK,确保其稳定性和兼容性。
- 权限设置:确保AndroidManifest.xml中添加了必要的BLE相关权限。
4.2 扫描与连接问题
- 扫描性能优化:使用合适的扫描周期和窗口,避免长时间扫描导致的资源消耗。
- 连接稳定性:在连接过程中处理各种异常情况,如连接超时、设备丢失等。
- 数据传输效率:优化数据包大小,避免过大的数据包导致的传输效率低下。
4.3 服务与特性交互问题
- 服务发现:在连接后及时发现设备上的服务。
- 特性读取与写入:注意处理权限问题,有些特性可能需要特定的权限才能操作。
- 特性通知:确保正确设置通知,以便中央设备能够及时获取更新。
4.4 安全性与隐私问题
- 数据加密:对敏感数据使用加密机制,确保数据传输的安全性。
- 设备配对与绑定:在设备配对时,使用适当的配对模式和加密级别。
- 隐私保护:尊重用户隐私,不收集无关个人信息。
4.5 BLE设备扫描与连接
- 设备扫描流程:分为主动扫描和被动扫描,主动扫描会请求设备发送完整的广告包。
- 设备连接机制:连接过程涉及GATT协议,设备间通过交换GATT服务进行通信。
- 扫描过滤与优化:设置扫描过滤条件可以提高扫描效率。
- 断开连接与重连策略:应用程序需要处理设备断开连接的情况,并实施重连策略。
4.5 BLE数据传输
- 数据读取与写入:数据读取通常涉及读取设备上的特征值,可能需要处理权限和配对过程。
- Notify与Indicate机制:Notify机制允许设备主动发送数据给应用程序,Indicate机制用于确保数据传输的可靠性。
- 数据加密与安全:在BLE通信中,可以使用加密算法来保护数据传输的安全。
- 数据传输错误处理:在数据传输过程中,可能会出现错误,需要实施错误处理策略。
4.6 最大传输单位MTU
- 此选项指定此选项的发送方能够为通道接受的最大SDU大小。
-
指定小于通道最小MTU的MTU的请求可能会被拒绝。
基本MTU交换
4.7 点对点连接和点对多点连接
-
一般只需要点对点连接即可
具有单个外设操作(a)、多外设操作(b)和分散网操作(c)
4.8 数据分包处理
- 由于部分产品不支持MTU修改,默认只能传输20字节,为兼容需要分包发送,防止数据丢失。
- 因为手动分包,Android数据延时不可控,需要在产品端加长延时
public static byte[][] splitByteArray(byte[] input, int chunkSize) {
if (input == null || chunkSize <= 0) {
throw new IllegalArgumentException("Invalid input: input cannot be null and chunkSize must be > 0");
}
int chunks = (input.length + chunkSize - 1) / chunkSize;
byte[][] result = new byte[chunks][];
for (int i = 0, start = 0; i < chunks; i++) {
int length = Math.min(input.length - start, chunkSize);
result[i] = new byte[length];
System.arraycopy(input, start, result[i], 0, length);
start += length;
}
return result;
}
5. Android BLE开发工具与库
- Android BLE API:Android提供了官方的BLE API,用于简化BLE设备的开发过程。
- 第三方BLE库:第三方库如BluetoothLe库,FastBle库,提供了更易用的API和更完善的错误处理机制。
- BLE调试工具:使用BLE调试宝,nRF Connect,可以帮助开发者快速定位和解决问题。
- 蓝牙核心文档下载
https://www.bluetooth.com/specifications/specs/ 搜索Core Specification,即可下载文档