Android BLE快速连接和断开以读取1个特性。一些BLE设备在快速连接断开连接后停止广播

时间:2016-07-28 03:13:41

标签: android bluetooth bluetooth-lowenergy

我正在尝试编写和Android应用程序扫描BLE设备,当它找到具有命名方案的某些设备时,它连接到它并读取特征(设备的用户定义名称),然后立即断开连接。然后,它将在列表中显示设备,其中包含找到的任何其他设备,并读取用户定义的名称。然后,用户可以选择要连接的设备(或多个设备)并连接到该设备并从中传输数据。

持续发生的问题是在获得用户定义的名称并且断开连接后,BLE设备停止广播,我在扫描时无法再找到它,或者在我读取用户定义的名称后尝试连接到它时断开连接。

这是Android BLE堆栈的问题还是我需要添加更多延迟(我使用的蓝牙服务有100毫秒的延迟)

以下是我在服务中使用的代码的一部分

 public boolean initialize() {

    Log.i(TAG, "Initializing");
    try {
        synchronized (Thread.currentThread()) {
            Thread.currentThread().wait(100);
        }
    }catch(InterruptedException e){
        //ignore
    }
    if (mBluetoothManager == null) {
        mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
        if (mBluetoothManager == null) {
            Log.e(TAG, "Unable to initialize BluetoothManager.");
            return false;
        }
    }

    mBluetoothAdapter = mBluetoothManager.getAdapter();
    if (mBluetoothAdapter == null) {
        Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
        return false;
    }


    mReadyToWrite = true;
    mReadyToRead = true;
    mReady = true;
    mCharacteristicWriteQueue   = new ArrayDeque<BluetoothGattCharacteristic>();
    mCharacteristicReadQueue    = new ArrayDeque<BluetoothGattCharacteristic>();
    mDescriptorWriteQueue       = new ArrayDeque<BluetoothGattDescriptor>();
    mDescriptorReadQueue        = new ArrayDeque<BluetoothGattDescriptor>();

    //mBluetoothGattMap = new HashMap<String, BluetoothGatt>();
    return true;
}

/**
 * Connects to the GATT server hosted on the Bluetooth LE device.
 *
 * @param address The device address of the device.
 *
 * @return Return true if the connection is initiated successfully. The connection result
 *         is reported asynchronously through the
 *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
 *         callback.
 */
public boolean connect(final String address) {
    if (mBluetoothAdapter == null || address == null) {
        Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
        return false;
    }



    if(mBluetoothGattMap.containsKey(address)) {
        Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
        if (mBluetoothGattMap.get(address).connect()) {
            mConnectionState = STATE_CONNECTING;
            return true;
        } else {
            return false;
        }
    }

    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
    if (device == null) {
        Log.w(TAG, "Device not found.  Unable to connect.");
        return false;
    }

    try {
        synchronized (Thread.currentThread()) {
            Thread.currentThread().wait(100);
        }
    }catch(InterruptedException e){
        //ignore
    }
    mBluetoothGattMap.put(address, device.connectGatt(this, false, mGattCallback));


    Log.d(TAG, "Trying to create a new connection to address " + address);
    //mBluetoothDeviceAddress = address;
    mConnectionState = STATE_CONNECTING;
    return true;
}

/**
 * Disconnects an existing connection or cancel a pending connection. The disconnection result
 * is reported asynchronously through the
 * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
 * callback.
 */
public void disconnect(String address) {
    if (mBluetoothAdapter == null || !mBluetoothGattMap.containsKey(address)) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    Log.i(TAG, "Disconnecting from gatt");
    try {
        synchronized (Thread.currentThread()) {
            Thread.currentThread().wait(100);
        }
    }catch(InterruptedException e){
        //ignore
    }
    mBluetoothGattMap.get(address).disconnect();
}


public void close(String address) {
    try {
        synchronized (Thread.currentThread()) {
            Thread.currentThread().wait(100);
        }
    }catch(InterruptedException e){
        //ignore
    }
    mBluetoothGattMap.get(address).close();
    mBluetoothGattMap.remove(address);
    Log.w(TAG, "Succeeed removing it");
}

public int getConnectionState(String address) {
    Log.i(TAG, "getting connection state for " + address);
    BluetoothGatt gatt = mBluetoothGattMap.get(address);
    return mBluetoothManager.getConnectionState(gatt.getDevice(), BluetoothProfile.GATT);
}

/**
 * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported
 * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
 * callback.
 *
 * @param characteristic The characteristic to read from.
 */
public void readCharacteristic(String address, BluetoothGattCharacteristic characteristic) {
    Log.i(TAG, "reading characteristic");
    if (mBluetoothAdapter == null || !mBluetoothGattMap.containsKey(address)) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    try {
        synchronized (Thread.currentThread()) {
            Thread.currentThread().wait(100);
        }
    }catch(InterruptedException e){
        //ignore
    }
    if(mReadyToRead && mReady) {
        boolean result = mBluetoothGattMap.get(address).readCharacteristic(characteristic);
        mReadyToRead = false;
        mReady = false;
        if(!result) {
            Log.i(TAG, "read failed");
        }
    }else {
        mCharacteristicReadQueue.push(characteristic);
    }
}

public void writeCharacteristic(String address, BluetoothGattCharacteristic characteristic) {
    Log.i(TAG, "writeCharacteristic - readyToWrite = " + mReadyToWrite + " queue size = " + mCharacteristicWriteQueue.size());
    if (mBluetoothAdapter == null || !mBluetoothGattMap.containsKey(address)) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    try {
        synchronized (Thread.currentThread()) {
            Thread.currentThread().wait(100);
        }
    }catch(InterruptedException e){
        //ignore
    }
    if(mReadyToWrite && mReady) {
        boolean result = mBluetoothGattMap.get(address).writeCharacteristic(characteristic);
        mReadyToWrite = false;
        mReady = false;
        if(!result) {
            Log.i(TAG, "characteristic write failed");
        }
    }else {
        mCharacteristicWriteQueue.push(characteristic);
    }
}

public void readDescriptor(String address, BluetoothGattDescriptor descriptor) {
    Log.i(TAG, "reading descriptor");
    if (mBluetoothAdapter == null || !mBluetoothGattMap.containsKey(address)) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    try {
        synchronized (Thread.currentThread()) {
            Thread.currentThread().wait(100);
        }
    }catch(InterruptedException e){
        //ignore
    }
    if(mReadyToRead && mReady) {
        boolean result = mBluetoothGattMap.get(address).readDescriptor(descriptor);
        mReadyToRead = false;
        mReady = false;
        if(!result) {
            Log.i(TAG, "descriptor read failed");
        }
    }else {
        mDescriptorReadQueue.push(descriptor);
    }
}

public void writeDescriptor(String address, BluetoothGattDescriptor descriptor) {
    Log.i(TAG, "writing descriptor for characteristic " + descriptor.getCharacteristic().getUuid().toString());
    if (mBluetoothAdapter == null || !mBluetoothGattMap.containsKey(address)) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    try {
        synchronized (Thread.currentThread()) {
            Thread.currentThread().wait(100);
        }
    }catch(InterruptedException e){
        //ignore
    }
    if(mReadyToWrite && mReady) {
        boolean result = mBluetoothGattMap.get(address).writeDescriptor(descriptor);
        mReadyToWrite = false;
        mReady = false;
        if(!result) {
            Log.i(TAG, "descriptor write failed");
        }
    }else {
        mDescriptorWriteQueue.push(descriptor);
    }
}

public BluetoothGattCharacteristic getCharacteristic(String address, UUID uuid) {
    if(!mBluetoothGattMap.containsKey(address)) {
        Log.i(TAG, "Device address " + address + " not found");
        return null;
    }
    try {
        synchronized (Thread.currentThread()) {
            Thread.currentThread().wait(100);
        }
    }catch(InterruptedException e){
        //ignore
    }
    for(BluetoothGattService service : mBluetoothGattMap.get(address).getServices()) {
        Log.i(TAG, "Service: " + service.getUuid().toString());
        for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
            Log.i(TAG, "Characteristic: " + characteristic.getUuid().toString());
            if(characteristic.getUuid().equals(uuid)) {
                return characteristic;
            }
        }
    }
    Log.i(TAG, "Characteristic not found");
    return null;
}

public Set<String> getConnectedDevices(){
    return this.mBluetoothGattMap.keySet();
}

/**
 * Enables or disables notification on a give characteristic.
 *
 * @param characteristic Characteristic to act on.
 * @param enabled If true, enable notification.  False otherwise.
 */
public void setCharacteristicNotification(String address, BluetoothGattCharacteristic characteristic, boolean enabled) {
    if (mBluetoothAdapter == null || !mBluetoothGattMap.containsKey(address)) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    try {
        synchronized (Thread.currentThread()) {
            Thread.currentThread().wait(100);
        }
    }catch(InterruptedException e){
        //ignore
    }
    mBluetoothGattMap.get(address).setCharacteristicNotification(characteristic, enabled);
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(GattAttributes.CLIENT_CHAR_CONFIG));
    if(descriptor != null) {
        boolean status = descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        Log.i(TAG, "descriptor " + descriptor.getUuid().toString() + " setValue() status: " + status);
        Log.i(TAG, "descriptor value: " + descriptor.getValue());
        writeDescriptor(address, descriptor);
    }
}

public void setPhoneEvents(byte priorities) {
    for(String address : mBluetoothGattMap.keySet()) {
        BluetoothGattCharacteristic characteristic = getCharacteristic(address, UUID.fromString(GattAttributes.ALERT_ATTRIBUTE));
        if (characteristic != null) {
            byte prioritiesBuf[] = new byte[1];
            prioritiesBuf[0] = priorities;
            characteristic.setValue(prioritiesBuf);
            writeCharacteristic(address, characteristic);
            Log.i(TAG, String.format("Forwarded phone alert priorities: 0x%X", priorities));
        } else {
            Log.e(TAG, "Failed to get the Alert ID characteristic from Gatt Server for device address " + address);
        }
    }
}

/**
 * Retrieves a list of supported GATT services on the connected device. This should be
 * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
 *
 * @return A {@code List} of supported services.
 */
public List<BluetoothGattService> getSupportedGattServices(String address) {
    if (mBluetoothGattMap.get(address) == null) return null;

    return mBluetoothGattMap.get(address).getServices();
}

2 个答案:

答案 0 :(得分:2)

  

持续发生的问题是在获得用户定义之后   名称和断开连接BLE设备停止广播,我可以   我扫描时不再找到它,或者在我之后尝试连接它   读取用户定义的名称并断开连接。

不应该是您的BLE设备需要在连接断开后立即开始投放广告? 我还建议您可以让您的BLE设备宣传除您的应用程序之外的自定义服务以进行连接然后阅读该特性;您只需使用“ScanFilter”过滤掉您喜欢的设备。 你只需要让低级代码对此做。

答案 1 :(得分:1)

观察到的行为实际上是BLE外围设备的一个功能,而不是Android,或者更一般地说是中央设备程序。

我最近一直在使用Laird BL600 - 它有一个“空中&#39; OTA”编程模式 - 当启用该模式时,该模块会宣传OTA服务打开电源后10秒钟即可:这就是它。在这种情况下,外围应用程序已被设计为在任何连接断开后不再回退到广告。要重新进入OTA模式,需要设备的电源循环。

正如郭所建议的那样,如果可以控制外围程序,那么BLE外设可以更方便地在其广告包中包含可读标识符(设备名称)和主要服务的GUID - 这将使过滤哪些设备更容易呈现给用户。

当用户希望连接到外围设备时,那只能使用device.connectGatt(this, false, mGattCallback)成功 如果该设备仍在广告和范围内。我认为这个电话中第二个参数的用意 - &#39; autoconnect&#39; - 是在Android中将连接标记为待处理 - 并且当设备重新进入范围和广告时连接应自动发生 - 但我发现这不是非常可靠。