You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 13:53:09