
Android BLE 开发实战连接 GATT 服务器与 20 字节数据分包传输在智能穿戴设备和物联网终端普及的今天低功耗蓝牙BLE已成为移动开发者必须掌握的核心技术之一。与经典蓝牙相比BLE 在保持稳定通信的同时显著降低了能耗这使得它成为心率监测器、智能门锁等需要长时间运行的设备的理想选择。本文将深入探讨 Android 平台上 BLE 开发的关键环节特别是如何高效连接 GATT 服务器并处理 20 字节 MTU 限制下的数据传输挑战。1. BLE 开发基础与环境准备1.1 权限声明与蓝牙适配器在 AndroidManifest.xml 中声明以下权限是 BLE 开发的第一步uses-permission android:nameandroid.permission.BLUETOOTH/ uses-permission android:nameandroid.permission.BLUETOOTH_ADMIN/ !-- Android 6.0 需要位置权限进行设备扫描 -- uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION/对于 Android 12 及以上版本权限系统更加细化uses-permission android:nameandroid.permission.BLUETOOTH_SCAN android:usesPermissionFlagsneverForLocation/ uses-permission android:nameandroid.permission.BLUETOOTH_CONNECT/获取 BluetoothAdapter 的实例BluetoothManager bluetoothManager (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter bluetoothAdapter bluetoothManager.getAdapter(); if (bluetoothAdapter null) { // 设备不支持蓝牙 return; }1.2 设备扫描与过滤使用 BluetoothLeScanner 进行设备扫描时可以通过 ScanFilter 提高效率BluetoothLeScanner scanner bluetoothAdapter.getBluetoothLeScanner(); ListScanFilter filters new ArrayList(); filters.add(new ScanFilter.Builder() .setServiceUuid(ParcelUuid.fromString(SERVICE_UUID)) .build()); ScanSettings settings new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .build(); scanner.startScan(filters, settings, scanCallback);关键参数对比扫描模式功耗发现速度适用场景SCAN_MODE_LOW_POWER低慢后台持续扫描SCAN_MODE_BALANCED中中一般应用SCAN_MODE_LOW_LATENCY高快需要快速响应的场景2. GATT 连接与服务发现2.1 建立 GATT 连接发现目标设备后通过 BluetoothDevice 对象建立连接BluetoothGatt gatt device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE);连接参数说明autoConnect设为 false 可加快连接速度TRANSPORT_LE确保使用低功耗传输模式2.2 服务发现流程连接成功后在 BluetoothGattCallback 中处理服务发现private final BluetoothGattCallback gattCallback 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) { ListBluetoothGattService services gatt.getServices(); // 处理发现的服务 } } };典型服务发现时间简单设备100-300ms复杂设备多服务500ms-1s3. 数据传输与 MTU 处理3.1 20 字节限制的挑战BLE 协议默认 MTU 为 23 字节扣除 ATT 头部的 3 字节后实际有效载荷仅剩 20 字节。这意味着单次传输数据量有限大文件传输需要分包机制需要处理数据重组和流量控制3.2 MTU 协商优化Android 5.0 支持 MTU 协商可提升单次传输量gatt.requestMtu(247); // 请求最大支持的MTU在回调中处理结果Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { if (status BluetoothGatt.GATT_SUCCESS) { Log.d(TAG, MTU changed to: mtu); } }MTU 大小对比MTU 大小有效载荷适用场景23 (默认)20 字节兼容所有设备158155 字节平衡兼容性与效率247 (最大)244 字节高性能设备3.3 数据分包与重组当传输数据超过 MTU 限制时需要实现分包机制。以下是发送端的示例public void sendLargeData(BluetoothGattCharacteristic characteristic, byte[] data) { int chunkSize 20; // 默认MTU限制 int offset 0; while (offset data.length) { int remaining data.length - offset; int length Math.min(remaining, chunkSize); byte[] chunk Arrays.copyOfRange(data, offset, offset length); characteristic.setValue(chunk); gatt.writeCharacteristic(characteristic); offset length; // 添加适当延迟避免堵塞 SystemClock.sleep(10); } }接收端需要实现数据重组逻辑ByteArrayOutputStream outputStream new ByteArrayOutputStream(); Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { byte[] chunk characteristic.getValue(); outputStream.write(chunk, 0, chunk.length); // 根据协议判断是否接收完成 if (isPacketComplete(outputStream.toByteArray())) { byte[] completeData outputStream.toByteArray(); processCompleteData(completeData); outputStream.reset(); } }4. 性能优化与异常处理4.1 连接参数优化BLE 的连接间隔Connection Interval直接影响功耗和吞吐量// Android 8.0 支持连接参数更新 gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);连接参数对比优先级连接间隔延迟功耗HIGH7.5-15ms低高BALANCED30-50ms中中LOW100-125ms高低4.2 常见问题排查连接失败处理检查蓝牙是否已开启验证权限是否已授予确认设备是否在范围内尝试重启蓝牙适配器数据传输稳定性优化添加重试机制实现数据校验如 CRC使用通知Notification代替读取避免在主线程执行蓝牙操作4.3 资源释放正确释放资源避免内存泄漏Override protected void onDestroy() { if (gatt ! null) { gatt.disconnect(); gatt.close(); } if (scanner ! null) { scanner.stopScan(scanCallback); } super.onDestroy(); }5. 实战健康设备数据采集案例以心率监测器为例演示完整的数据采集流程5.1 服务与特征值定义// 心率服务UUID private static final UUID HEART_RATE_SERVICE UUID.fromString(0000180D-0000-1000-8000-00805f9b34fb); // 心率测量特征值UUID private static final UUID HEART_RATE_MEASUREMENT UUID.fromString(00002A37-0000-1000-8000-00805f9b34fb);5.2 启用通知BluetoothGattService hrService gatt.getService(HEART_RATE_SERVICE); BluetoothGattCharacteristic hrChar hrService .getCharacteristic(HEART_RATE_MEASUREMENT); // 启用通知 gatt.setCharacteristicNotification(hrChar, true); // 写入描述符启用通知 BluetoothGattDescriptor descriptor hrChar .getDescriptor(CLIENT_CHARACTERISTIC_CONFIG); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); gatt.writeDescriptor(descriptor);5.3 数据处理Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { if (HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { byte[] data characteristic.getValue(); int flags data[0]; int heartRate; if ((flags 0x01) ! 0) { // 16位心率值 heartRate (data[1] 0xFF) ((data[2] 0xFF) 8); } else { // 8位心率值 heartRate data[1] 0xFF; } updateHeartRateDisplay(heartRate); } }在实际项目中遇到的最棘手问题往往是不同厂商对蓝牙协议实现的差异。某次开发中我们发现华为设备在连接某些BLE设备时会出现间歇性断开最终通过调整连接间隔和超时设置解决了这个问题。这种经验告诉我们蓝牙开发不能仅停留在API调用层面还需要深入理解协议细节和设备特性。