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

Android Studio新手求助:发送蓝牙十六进制命令`87`后无法接收数据(onCharacteristicChanged未触发)

Android Studio新手求助:发送蓝牙十六进制命令87后无法接收数据(onCharacteristicChanged未触发)

嗨,我仔细看了你的代码,发现几个关键问题导致onCharacteristicChanged没触发,咱们一步步来修正:

先说说核心问题点

  1. 盲目遍历所有特征设置通知:你现在给每个遍历到的特征都加通知,不仅没必要,还可能把通知挂到错误的特征上,设备返回的数据根本没发到你监听的特征。
  2. CCCD描述符UUID用错:在setNotify方法里你写了0000ffe1-...,但标准的客户端特征配置描述符(CCCD)UUID是固定的00002902-0000-1000-8000-00805f9b34fb,这个是BLE协议规定的,不能乱改。
  3. 变量作用域错误:onServicesDiscovered末尾调用通知设置时,用的是循环最后一个characteristic变量,大概率不是你要操作的目标特征。
  4. 缺少发送0x87的具体实现:sendSetting()是空的,没把十六进制命令转换成byte数组写入特征。

修改后的完整代码片段

首先先定义目标特征的UUID(这里一定要替换成你的蓝牙设备文档里指定的可写+通知特征UUID,比如常见的BLE串口特征是0000ffe1-0000-1000-8000-00805f9b34fb):

// 替换成你的设备实际的特征UUID
private static final UUID TARGET_CHAR_UUID = UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb");
private BluetoothGattCharacteristic mTargetCharacteristic; // 保存目标特征,方便后续调用

然后修改BluetoothGattCallback里的关键方法:

BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    @SuppressLint("MissingPermission")
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
        Log.i(TAG, "onConnectionStateChange newstate:" + newState + " status:" + status);
        if (status == BluetoothGatt.GATT_SUCCESS) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                Log.i(TAG, "============>GATT Connect Success!!<=============");
                mBluetoothGatt.discoverServices();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                if (mBluetoothGatt != null) {
                    mBluetoothGatt.close();
                    mBluetoothGatt = null;
                    mTargetCharacteristic = null; // 断开后清空特征引用
                }
            }
        }
    }

    @SuppressLint("MissingPermission")
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        Log.i(TAG, "onServicesDiscovered(), status = " + status);
        
        if (status == BluetoothGatt.GATT_SUCCESS) {
            // 只找我们需要的目标特征,不要遍历所有特征
            for (BluetoothGattService service : gatt.getServices()) {
                mTargetCharacteristic = service.getCharacteristic(TARGET_CHAR_UUID);
                if (mTargetCharacteristic != null) {
                    Log.i(TAG, "找到目标特征:" + TARGET_CHAR_UUID);
                    // 设置通知监听
                    setupNotification(mTargetCharacteristic);
                    // 发送0x87命令
                    sendHexCommand(0x87);
                    break; // 找到目标后直接退出循环
                }
            }
            if (mTargetCharacteristic == null) {
                Log.w(TAG, "未找到目标特征,请检查UUID是否和设备文档一致");
            }
        } else {
            Log.i(TAG, "onServicesDiscovered status false");
        }
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) {
        super.onCharacteristicChanged(gatt, characteristic, value);
        String receivedData = new String(value, StandardCharsets.UTF_8); 
        Log.d(TAG, "Received data: " + receivedData);
        // 这里可以处理收到的数据,比如更新UI
        runOnUiThread(() -> {
            // 示例:显示收到的数据
            // mTvReceived.setText("收到数据:" + receivedData);
        });
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicWrite(gatt, characteristic, status);
        if(status == BluetoothGatt.GATT_SUCCESS){
            Log.i(TAG,"onCharacteristicWrite status : " + status);
            Log.i(TAG,"value write success !!");
        }
        else{
            Log.i(TAG,"onCharacteristicWrite status : " + status);
            Log.w(TAG,"value write false !!");
        }
    }

    // 其他回调方法(onCharacteristicRead)保留原逻辑即可
};

然后添加两个关键工具方法:

// 统一处理通知设置逻辑
@SuppressLint("MissingPermission")
private void setupNotification(BluetoothGattCharacteristic characteristic) {
    // 打开特征通知开关
    mBluetoothGatt.setCharacteristicNotification(characteristic, true);
    // 获取标准CCCD描述符
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
    if (descriptor != null) {
        // 根据特征支持的类型选择通知/指示
        if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        } else if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
        }
        // 写入描述符
        boolean success = mBluetoothGatt.writeDescriptor(descriptor);
        Log.i(TAG, "设置通知:" + (success ? "成功" : "失败"));
    } else {
        Log.w(TAG, "该特征没有CCCD描述符,无法设置通知");
    }
}

// 发送十六进制命令
@SuppressLint("MissingPermission")
private void sendHexCommand(int hexValue) {
    if (mTargetCharacteristic == null) {
        Log.w(TAG, "目标特征为空,无法发送命令");
        return;
    }
    // 检查特征是否支持写操作
    if ((mTargetCharacteristic.getProperties() & (BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) == 0) {
        Log.w(TAG, "该特征不支持写操作");
        return;
    }
    // 把十六进制87转换成byte数组
    byte[] command = new byte[]{(byte) hexValue};
    mTargetCharacteristic.setValue(command);
    // 写入特征(根据设备支持选择WRITE或WRITE_NO_RESPONSE)
    boolean success = mBluetoothGatt.writeCharacteristic(mTargetCharacteristic);
    Log.i(TAG, "发送命令0x87:" + (success ? "成功" : "失败"));
}

最后别忘了检查权限:在AndroidManifest.xml里添加必要的蓝牙权限(针对Android 12+):

<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

额外注意事项

  1. 一定要确认目标特征UUID:这个UUID必须和你的蓝牙设备厂商提供的文档一致,不能随便用示例UUID,否则找不到特征。
  2. 测试时看日志:如果还是没触发onCharacteristicChanged,看日志里有没有“未找到目标特征”或“设置通知失败”的提示,排查问题。
  3. 设备是否主动发数据:有些设备需要收到命令后才会返回数据,确保你发送0x87后设备确实有数据输出。

备注:内容来源于stack exchange,提问作者CXH

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.20 02:39:32