Android nougat(超过7.0.0)的BLE(蓝牙低功耗)无法读取数据

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android nougat(超过7.0.0)的BLE(蓝牙低功耗)无法读取数据相关的知识,希望对你有一定的参考价值。

首先,我的应用程序无法使用BLE读取牛轧糖中的数据我正在使用BlueNRG-MS模块与设备连接。调用Marshmallow onCharacteristicChanged方法,我可以接收数据。但超过7.0.0 onCharacteristicChanged方法从未调用过。

我搜索了这个,有人告诉我添加这个代码。

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_UUID);
        if (descriptor != null) {
            descriptor.setValue(enabled ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
            mBluetoothGatt.writeDescriptor(descriptor);
        }

但我不确切知道在CLIENT_UUID中输入什么。

这就是我的所有代码。

public class BleManager {

private static final String TAG = "BleManager";

static final private UUID CCCD_ID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

public static final int STATE_ERROR = -1;
public static final int STATE_NONE = 0;     // Initialized
public static final int STATE_IDLE = 1;     // Not connected
public static final int STATE_SCANNING = 2; // Scanning
public static final int STATE_CONNECTING = 13;  // Connecting
public static final int STATE_CONNECTED = 16;   // Connected

public static final int MESSAGE_STATE_CHANGE = 1;
public static final int MESSAGE_READ = 2;
public static final int MESSAGE_WRITE = 3;
public static final int MESSAGE_DEVICE_NAME = 4;
public static final int MESSAGE_TOAST = 5;

public static final long SCAN_PERIOD = 5*1000;

private static Context mContext = null;
private static BleManager mBleManager = null;
private final Handler mHandler;

private final BluetoothAdapter mBluetoothAdapter;
private BluetoothAdapter.LeScanCallback mLeScanCallback = null;

private ArrayList<BluetoothDevice> mDeviceList = new ArrayList<BluetoothDevice>();
private BluetoothDevice mDefaultDevice = null;

private BluetoothGatt mBluetoothGatt = null;

private ArrayList<BluetoothGattService> mGattServices 
        = new ArrayList<BluetoothGattService>();
private ArrayList<BluetoothGattCharacteristic> mGattCharacteristics 
        = new ArrayList<BluetoothGattCharacteristic>();
private ArrayList<BluetoothGattCharacteristic> mWritableCharacteristics 
        = new ArrayList<BluetoothGattCharacteristic>();
private BluetoothGattCharacteristic mDefaultChar = null;

private int mState = -1;

/**
 * Constructor. Prepares a new Bluetooth session.
 * @param context  The UI Activity Context
 * @param h  A Listener to receive messages back to the UI Activity
 */
private BleManager(Context context, Handler h) {
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    mState = STATE_NONE;
    mHandler = h;
    mContext = context;

    if(mContext == null)
        return;
}

public synchronized static BleManager getInstance(Context c, Handler h) {
    if(mBleManager == null)
        mBleManager = new BleManager(c, h);

    return mBleManager;
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public synchronized void finalize() {
    if (mBluetoothAdapter != null) {
        mState = STATE_IDLE;
        mBluetoothAdapter.stopLeScan(mLeScanCallback);
        disconnect();
    }

    mDefaultDevice = null;
    mBluetoothGatt = null;
    mDefaultService = null;
    mGattServices.clear();
    mGattCharacteristics.clear();
    mWritableCharacteristics.clear();

    if(mContext == null)
        return;

}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void stopScanning() {
    if(mState < STATE_CONNECTING) {
        mState = STATE_IDLE;
        mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_IDLE, 0).sendToTarget();
    }
    mBluetoothAdapter.stopLeScan(mLeScanCallback);
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private int checkGattServices(List<BluetoothGattService> gattServices) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.d(TAG, "# BluetoothAdapter not initialized");
        return -1;
    }

    for (BluetoothGattService gattService : gattServices) {
        Log.d(TAG, "# GATT Service: "+gattService.toString());
        Toast.makeText(mContext, "" + gattService.toString(), Toast.LENGTH_SHORT).show();

        mGattServices.add(gattService);

        List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
        for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
            Toast.makeText(mContext, "" + gattCharacteristic.toString(), Toast.LENGTH_SHORT).show();
            mGattCharacteristics.add(gattCharacteristic);
            Log.d(TAG, "# GATT Char: "+gattCharacteristic.toString());

            boolean isWritable = isWritableCharacteristic(gattCharacteristic);
            if(isWritable) {
                mWritableCharacteristics.add(gattCharacteristic);
            }

            boolean isReadable = isReadableCharacteristic(gattCharacteristic); 
            if(isReadable) {
                readCharacteristic(gattCharacteristic);
            }

            if(isNotificationCharacteristic(gattCharacteristic)) {
                setCharacteristicNotification(gattCharacteristic, true);
                if(isWritable && isReadable) {
                    mDefaultChar = gattCharacteristic;
                }
            }
        }
    }

    return mWritableCharacteristics.size();
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private boolean isWritableCharacteristic(BluetoothGattCharacteristic chr) {
    if(chr == null) return false;

    final int charaProp = chr.getProperties();
    if (((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) |
            (charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) > 0) {
        Log.d(TAG, "# Found writable characteristic");
        return true;
    } else {
        Log.d(TAG, "# Not writable characteristic");
        return false;
    }
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private boolean isReadableCharacteristic(BluetoothGattCharacteristic chr) {
    if(chr == null) return false;

    final int charaProp = chr.getProperties();
    if((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
        Log.d(TAG, "# Found readable characteristic");
        return true;
    } else {
        Log.d(TAG, "# Not readable characteristic");
        return false;
    }
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private boolean isNotificationCharacteristic(BluetoothGattCharacteristic chr) {
    if(chr == null) return false;

    final int charaProp = chr.getProperties();
    if((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
        Log.d(TAG, "# Found notification characteristic");
        return true;
    } else {
        Log.d(TAG, "# Not notification characteristic");
        return false;
    }
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.d(TAG, "# BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.readCharacteristic(characteristic);
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
                                          boolean enabled) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.d(TAG, "# BluetoothAdapter not initialized");
        return;
    }

    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCCD_ID);
    if (descriptor != null) {
        descriptor.setValue(enabled ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        mBluetoothGatt.writeDescriptor(descriptor);
    }
}


public void setScanCallback(BluetoothAdapter.LeScanCallback cb) {
    mLeScanCallback = cb;
}

public int getState() {
    return mState;
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean scanLeDevice(final boolean enable) {
    boolean isScanStarted = false;
    if (enable) {
        if(mState == STATE_SCANNING)
            return false;

        if(mBluetoothAdapter.startLeScan(mLeScanCallback)) {
            mState = STATE_SCANNING;
            mDeviceList.clear();

            mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        stopScanning();
                    }
                }, SCAN_PERIOD);

            mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_SCANNING, 0).sendToTarget();
            isScanStarted = true;
        }
    } else {
        if(mState < STATE_CONNECTING) {
            mState = STATE_IDLE;
            mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_IDLE, 0).sendToTarget();
        }
        stopScanning();
    }

    return isScanStarted;
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean connectGatt(Context c, boolean bAutoReconnect, BluetoothDevice device) {
    if(c == null || device == null)
        return false;

    mGattServices.clear();
    mGattCharacteristics.clear();
    mWritableCharacteristics.clear();

    mBluetoothGatt = device.connectGatt(c, bAutoReconnect, mGattCallback);
    mDefaultDevice = device;

    mState = STATE_CONNECTING;
    mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_CONNECTING, 0).sendToTarget();
    return true;
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean connectGatt(Context c, boolean bAutoReconnect, String address) {
    if(c == null || address == null)
        return false;

    if(mBluetoothGatt != null && mDefaultDevice != null
            && address.equals(mDefaultDevice.getAddress())) {
         if (mBluetoothGatt.connect()) {
             mState = STATE_CONNECTING;
             return true;
         }
    }

    BluetoothDevice device = 
            BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
    if (device == null) {
        Log.d(TAG, "# Device not found.  Unable to connect.");
        return false;
    }

    mGattServices.clear();
    mGattCharacteristics.clear();
    mWritableCharacteristics.clear();

    mBluetoothGatt = device.connectGatt(c, bAutoReconnect, mGattCallback);
    mDefaultDevice = device;

    mState = STATE_CONNECTING;
    mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_CONNECTING, 0).sendToTarget();
    return true;
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void disconnect() {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.d(TAG, "# BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.disconnect();
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean write(BluetoothGattCharacteristic chr, byte[] data) {
    if (mBluetoothGatt == null) {
        Log.d(TAG, "# BluetoothGatt not initialized");
        return false;
    }

    BluetoothGattCharacteristic writableChar = null;

    if(chr == null) {
        if(mDefaultChar == null) {
            for(BluetoothGattCharacteristic bgc : mWritableCharacteristics) {
                if(isWritableCharacteristic(bgc)) {
                    writableChar = bgc;
                }
            }
            if(writableChar == null) {
                Log.d(TAG, "# Write failed - No available characteristic");
                return false;
            }
        } else {
            if(isWritableCharacteristic(mDefaultChar)) {
                Log.d(TAG, "# Default GattCharacteristic is PROPERY_WRITE | PROPERTY_WRITE_NO_RESPONSE");
                writableChar = mDefaultChar;
            } else {
                Log.d(TAG, "# Default GattCharacteristic is not writable");
                mDefaultChar = null;
                return false;
            }
        }
    } else {
        if (isWritableCharacteristic(chr)) {
            Log.d(TAG, "# user GattCharacteristic is PROPERY_WRITE | PROPERTY_WRITE_NO_RESPONSE");
            writableChar = chr;
        } else {
            Log.d(TAG, "# user GattCharacteristic is not writable");
            return false;
        }
    }

    writableChar.setValue(data);
    writableChar.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
    mBluetoothGatt.writeCharacteristic(writableChar);
    mDefaultChar = writableChar;
    return true;
}

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            mState = STATE_CONNECTED;
            Log.d(TAG, "# Connected to GATT server.");

            mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_CONNECTED, 0).sendToTarget();

            gatt.discoverServices();

        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            mState = STATE_IDLE;
            Log.d(TAG, "# Disconnected from GATT server.");
            mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_IDLE, 0).sendToTarget();
            mBluetoothGatt = null;
            mGattServices.clear();
            mDefaultService = null;
            mGattCharacteristics.clear();
            mWritableCharacteristics.clear();
            mDefaultChar = null;
            mDefaultDevice = null;
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.d(TAG, "# New GATT service discovered.");
            checkGattServices(gatt.getServices());
        } else {
            Log.d(TAG, "# onServicesDiscovered received: " + status);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            // We've received data from remote
            Log.d(TAG, "# Read characteristic: "+characteristic.toString());

            final byte[] data = characteristic.getValue();
            if (data != null && data.length > 0) {
                final StringBuilder stringBuilder = new StringBuilder(data.length);
                stringBuilder.append(data);
                Log.d(TAG, stringBuilder.toString());

                mHandler.obtainMessage(MESSAGE_READ, byteArrayToHex(data)).sendToTarget();
            }

            if(mDefaultChar == null && isWritableCharacteristic(characteristic)) {
                mDefaultChar = characteristic;
            }
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        // We've received data from remote
        Log.d(TAG, "# onCharacteristicChanged: "+characteristic.toString());

        final byte[] data = characteristic.getValue();
        if (data != null && data.length > 0) {
            final StringBuilder stringBuilder = new StringBuilder(data.length);
            //for(byte byteChar : data)
            //  stringBuilder.append(String.format("%02X ", byteChar));
            stringBuilder.append(data);
            Log.d(TAG, stringBuilder.toString());

            mHandler.obtainMessage(MESSAGE_READ, byteArrayToHex(data)).sendToTarget();
        }

        if(mDefaultChar == null && isWritableCharacteristic(characteristic)) {
            mDefaultChar = characteristic;
        }
    }
};

String byteArrayToHex(byte[] a) {
    StringBuilder sb = new StringBuilder();
    for(final byte b: a)
        sb.append(String.format("%02x", b&0xff));
    return sb.toString();
}}
答案

请尝试以下功能启用蓝牙低功耗特性通知。我没有注意到android 7.0.0以上的任何麻烦。

当您订阅特征更新时,您应该在onCharacteristicChanged Callback()中获取更新的值。确保,外围设备上的值实际更新;)

private void enableNotifications(BluetoothGatt bluetoothGatt, BluetoothGattCharacteristic characteristic ){

    UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

    byte[] payload =  BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;

    bluetoothGatt.setCharacteristicNotification(characteristic, true);

    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
            CLIENT_CHARACTERISTIC_CONFIG);

    if (descriptor == null){
        Log.w(TAG, "Notification not supported for characteristic");
        return;
    }

    descriptor.setValue(payload);

    bluetoothGatt.writeDescriptor(descriptor);
}

以上是关于Android nougat(超过7.0.0)的BLE(蓝牙低功耗)无法读取数据的主要内容,如果未能解决你的问题,请参考以下文章

Android 7.0 (Nougat) 打盹模式停止 Web 服务

仅从 Android 7.0 (Nougat) 开始支持默认接口方法

Nougat 7 不支持 Android 相机裁剪

android 7.0 (nougat)的编译优化-ninja

java 所有Android 7.X Nougat权限

Android 警报管理器 setExactAndAllowWhileIdle() 在打盹模式下的 Android 7.0 Nougat 中不起作用