Android BLE:GattServer端无法获取MTU且设备亮度控制无响应
Android BLE MTU协商及设备控制问题排查
问题现象
- 客户端通过
requestMtu()发起MTU协商,能在onMtuChanged()回调收到通知,但GattServer端无法获取当前MTU值 - 开发的设备亮度控制功能调用后无响应;移除
requestMtu()相关代码后,日志能正常输出,功能恢复正常
相关代码
命令发送方法
public static final String SERVICE_GUID = "6d1b33f8-0000-11eb-a8b3-0242ac130003"; public static final String TX_CHAR_GUID = "6d1b33f8-0014-11eb-a8b3-0242ac130003"; public void sendCommands(BluetoothGatt gatt) throws JSONException { if (bluetoothGatt != null) { List<BluetoothGattService> gattServices = bluetoothGatt.getServices(); if (2 == BluetoothProfile.STATE_CONNECTED) // BLE Is connected after provision as ghatt is connected. { final Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { // TODO: Consider calling // ActivityCompat#requestPermissions // here to request the missing permissions, and then overriding // public void onRequestPermissionsResult(int requestCode, String[] permissions, // int[] grantResults) // to handle the case where the user grants the permission. See the documentation // for ActivityCompat#requestPermissions for more details. return; } gatt.requestMtu(512); } }, 20000); for (int i = 0; i < gattServices.size(); i++) { String serviceUUID = gattServices.get(i).getUuid().toString(); if (serviceUUID.equalsIgnoreCase(SERVICE_GUID)) { List<BluetoothGattCharacteristic> gattCharacteristics = gattServices.get(i).getCharacteristics(); for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { final String uuid = gattCharacteristic.getUuid().toString(); if (uuid.equalsIgnoreCase(TX_CHAR_GUID)) { String jsonString = "{\"i\":1,\"v\":1,\"t\":8,\"s\":\"dbri\",\"n\":50}"; byte[] payload = jsonString.getBytes(Charsets.UTF_8); gattCharacteristic.setValue(payload); /*System.out.println("Length:"+payload.length); try { JSONObject obj = new JSONObject(jsonString); String byte_one=obj.getString("i"); System.out.println("byte_one:"+byte_one); byte[] payload1 = byte_one.getBytes(); } catch (Throwable t) { // Log.e("My App", "Could not parse malformed JSON: \"" + obj + "\""); }*/ gatt.writeCharacteristic(gattCharacteristic); } } } } } } }
MTU变更回调
@Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { // TODO: Consider calling // ActivityCompat#requestPermissions // here to request the missing permissions, and then overriding // public void onRequestPermissionsResult(int requestCode, String[] permissions, // int[] grantResults) // to handle the case where the user grants the permission. See the documentation // for ActivityCompat#requestPermissions for more details. return; } gatt.requestMtu(mtu); Log.i(TAG, "New MTU: " + mtu + " , Status: " + status + " , Succeed: " + (status == gatt.GATT_SUCCESS)); }
问题分析与解决建议
1. 无限循环调用requestMtu()导致通道阻塞
在onMtuChanged()回调中每次都调用gatt.requestMtu(mtu),会触发新一轮MTU协商,进而再次回调onMtuChanged(),形成无限循环。这会占用BLE通信通道,导致亮度控制命令无法正常发送,甚至阻塞日志输出。
修复方案:移除回调内的gatt.requestMtu(mtu),仅在需要时发起MTU请求,比如初始连接后一次请求:
@Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { return; } Log.i(TAG, "New MTU: " + mtu + " , Status: " + status + " , Succeed: " + (status == gatt.GATT_SUCCESS)); // MTU协商成功后再发送亮度控制命令,确保适配最新MTU if (status == BluetoothGatt.GATT_SUCCESS) { try { sendCommands(gatt); } catch (JSONException e) { e.printStackTrace(); } } }
2. 命令发送时机冲突
当前代码在连接成功后立即发送命令,同时延迟20秒发起MTU协商,协商过程中BLE通道忙碌,可能导致命令发送失败。
修复方案:调整逻辑,等待MTU协商完成后再发送命令。拆分原方法,单独处理MTU初始化和命令发送:
// 连接成功后调用此方法发起MTU协商 public void initMtuAfterConnect(BluetoothGatt gatt) { if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { return; } gatt.requestMtu(512); } // 修改sendCommands,仅保留命令发送逻辑 public void sendCommands(BluetoothGatt gatt) throws JSONException { if (bluetoothGatt == null || BluetoothProfile.STATE_CONNECTED != bluetoothGatt.getConnectionState()) { return; } List<BluetoothGattService> gattServices = bluetoothGatt.getServices(); for (BluetoothGattService service : gattServices) { String serviceUUID = service.getUuid().toString(); if (serviceUUID.equalsIgnoreCase(SERVICE_GUID)) { for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) { String uuid = characteristic.getUuid().toString(); if (uuid.equalsIgnoreCase(TX_CHAR_GUID)) { String jsonString = "{\"i\":1,\"v\":1,\"t\":8,\"s\":\"dbri\",\"n\":50}"; byte[] payload = jsonString.getBytes(Charsets.UTF_8); characteristic.setValue(payload); gatt.writeCharacteristic(characteristic); Log.i(TAG, "Brightness control command sent, payload length: " + payload.length); } } } } }
3. GattServer端获取MTU值的方法
GattServer端可通过重写BluetoothGattServerCallback的onMtuChanged()回调获取MTU值,该回调会在客户端发起协商时触发:
@Override public void onMtuChanged(BluetoothDevice device, int mtu) { super.onMtuChanged(device, mtu); Log.i(TAG, "Device " + device.getAddress() + " MTU changed to: " + mtu); // 可在此处保存MTU值供后续业务使用 }
内容的提问来源于stack exchange,提问作者Hayjyo
相关产品推荐
相关产品推荐

