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

Android Studio+Java开发BLE设备特征属性管理问题求助

BLE特征读/写/通知属性管理问题解决方案

一、修正Properties.java的属性判断逻辑

BLE特征的属性是位掩码组合,不能直接用==判断,必须通过&位运算校验。以下是修复后的完整代码:

import android.bluetooth.BluetoothGattCharacteristic;

public class Properties {
    private final int rawProperties;

    public Properties(int rawProperties) {
        this.rawProperties = rawProperties;
    }

    // 判断是否支持读操作
    public boolean supportsRead() {
        return (rawProperties & BluetoothGattCharacteristic.PROPERTY_READ) != 0;
    }

    // 判断是否支持写操作(包含带响应/无响应两种类型)
    public boolean supportsWrite() {
        return (rawProperties & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0
                || (rawProperties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0;
    }

    // 区分带响应写
    public boolean supportsWriteWithResponse() {
        return (rawProperties & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0;
    }

    // 区分无响应写
    public boolean supportsWriteWithoutResponse() {
        return (rawProperties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0;
    }

    // 判断是否支持通知
    public boolean supportsNotification() {
        return (rawProperties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0;
    }

    // 判断是否支持指示
    public boolean supportsIndication() {
        return (rawProperties & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0;
    }
}

二、优化GattManager.java的实例管理与操作校验

解决Gatt实例泄漏、操作无校验、通知配置不完整等问题,修复后的代码如下:

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.content.Context;
import android.util.Log;
import java.util.UUID;

public class GattManager {
    private static GattManager instance;
    private BluetoothGatt mGatt;
    private Context mContext;
    private BluetoothDevice mConnectedDevice;

    private GattManager(Context context) {
        mContext = context.getApplicationContext();
    }

    // 单例模式,避免重复创建Gatt实例
    public static synchronized GattManager getInstance(Context context) {
        if (instance == null) {
            instance = new GattManager(context);
        }
        return instance;
    }

    // 连接设备,自动关闭旧实例防止泄漏
    public boolean connect(BluetoothDevice device) {
        if (mGatt != null) {
            mGatt.close();
            mGatt = null;
        }
        mConnectedDevice = device;
        mGatt = device.connectGatt(mContext, false, mGattCallback);
        return mGatt != null;
    }

    // 断开连接并释放所有资源
    public void disconnect() {
        if (mGatt != null) {
            mGatt.disconnect();
            mGatt.close();
            mGatt = null;
            mConnectedDevice = null;
        }
    }

    // 读取特征:先校验属性,避免无效调用
    public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mGatt == null || characteristic == null) {
            return false;
        }
        Properties props = new Properties(characteristic.getProperties());
        if (!props.supportsRead()) {
            Log.e("GattManager", "特征不支持读操作");
            return false;
        }
        return mGatt.readCharacteristic(characteristic);
    }

    // 写入特征:根据属性匹配写类型,避免写入失败
    public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic, byte[] data) {
        if (mGatt == null || characteristic == null || data == null) {
            return false;
        }
        Properties props = new Properties(characteristic.getProperties());
        if (!props.supportsWrite()) {
            Log.e("GattManager", "特征不支持写操作");
            return false;
        }
        characteristic.setValue(data);
        // 根据属性设置对应的写类型
        if (props.supportsWriteWithResponse()) {
            characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
        } else if (props.supportsWriteWithoutResponse()) {
            characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
        }
        return mGatt.writeCharacteristic(characteristic);
    }

    // 设置通知/指示:自动处理CCCD描述符配置
    public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable) {
        if (mGatt == null || characteristic == null) {
            return false;
        }
        Properties props = new Properties(characteristic.getProperties());
        if (!props.supportsNotification() && !props.supportsIndication()) {
            Log.e("GattManager", "特征不支持通知/指示");
            return false;
        }
        // 先启用Gatt层通知开关
        if (!mGatt.setCharacteristicNotification(characteristic, enable)) {
            return false;
        }
        // 配置CCCD描述符(通知/指示的核心开关)
        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
        if (descriptor != null) {
            byte[] value;
            if (props.supportsIndication()) {
                value = enable ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
            } else {
                value = enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
            }
            descriptor.setValue(value);
            return mGatt.writeDescriptor(descriptor);
        }
        return false;
    }

    // 自定义Gatt回调,处理操作结果
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            // 这里可以添加连接状态回调逻辑,比如通知上层界面
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                // 读取成功,处理特征数据
            } else {
                Log.e("GattCallback", "读特征失败,状态码:" + status);
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                // 写入成功
            } else {
                Log.e("GattCallback", "写特征失败,状态码:" + status);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            // 收到通知/指示数据,处理逻辑
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            // 处理CCCD写入结果
        }
    };
}

三、核心注意事项

  1. 位掩码判断属性:BLE特征属性是多值组合,必须用&位运算校验,不能直接用==,否则会忽略多属性共存的情况。
  2. Gatt实例单例管理:每次连接前关闭旧实例,避免资源泄漏和操作冲突。
  3. 操作前属性校验:所有读、写、通知操作必须先通过Properties类校验特征支持性,防止无效调用导致异常。
  4. 写类型匹配:根据特征的写属性设置对应的writeType,否则会导致写入失败。
  5. CCCD描述符配置:启用通知/指示必须写入CCCD描述符,且要区分通知和指示的不同值。

内容的提问来源于stack exchange,提问作者navodsamu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 10:08:18