2016-07-28 129 views
2

我正在嘗試編寫和掃描BLE設備的Android應用程序,以及何時發現某些具有命名方案的設備連接到它並讀取特徵(用戶定義的名稱爲該設備)然後立即斷開連接。然後它會將設備顯示在列表中,並找到任何其他設備並讀取用戶定義的名稱。然後用戶可以選擇一個設備連接到(或多個設備)並連接到它並從中傳輸數據。Android BLE快速連接和斷開連接以讀取1個特徵。有些BLE設備在快速連接斷開連接後停止播放

繼續發生的問題是在獲取用戶定義的名稱並斷開連接後,BLE設備停止播放,並且當我掃描或在讀取用戶定義的名稱後嘗試連接時,我無法再找到它並斷開連接。

這是與Android BLE棧的問題,還是需要增加更多的延遲(我已遍及我使用bluetoothservice 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(); 
} 
+0

你有沒有解決這個問題?我有類似的問題...我斷開'BluetoothGatt',然後當呼叫表明斷開,我關閉'BluetoothGatt',但它似乎連接實際上並沒有斷開/關閉。一旦關閉我的應用程序,連接關閉,外圍設備返回廣告,所以我知道外設的行爲是正確的。 – mtrewartha

回答

1

這種情況持續發生的問題是它會在用戶定義的 名和斷開的BLE裝置停止廣播,我可以 再也找不到它,當我掃描或如果我嘗試之後我連接到它後 讀取用戶定義的名稱並斷開連接。

不應該是您的BLE設備在連接斷開後不久需要啓動廣告嗎? 我也建議你可以讓你的BLE設備宣傳你的定製服務,而不是你的應用程序連接然後閱讀特性;你只需使用「ScanFilter」來過濾你最喜歡的設備。 你只是讓低級代碼做到這一點。

+0

斷開連接後的某些設備不會廣播。我必須重新啓動它們才能將它們帶回 –

+0

瞭解。對於這種情況,即使你拖延時間,設備應該仍然沒有播出權?爲什麼不使用相同的行爲設備。 –

1

觀察到的行爲實際上是BLE外圍設備的一項功能,而不是Android,或者更一般的中央設備程序。

我最近一直在使用Laird BL600--它有一個'Over the Air',OTA,編程模式 - 並且當啓用該模式時,該模塊在被供電後廣告OTA服務長達10秒在:就是這樣。在這種情況下,外設應用程序已被設定爲在任何連接斷開後不會退出廣告。要重新進入OTA模式,需要設備的重新啓動。根據Guo的建議,如果能夠控制外設程序,BLE外設就可以在其廣告數據包中包含一個可讀標識符(設備名稱)和主服務的GUID--這將使過濾哪些設備更容易呈現給用戶。

當用戶希望連接到外圍設備時,如果設備仍在廣告範圍內,那麼只有使用device.connectGatt(this, false, mGattCallback) 才能成功。我認爲這個調用中的第二個參數 - 'autoconnect'的意圖是在Android中將連接標記爲待定 - 並且在設備返回到範圍和廣告時連接應該自動發生 - 但是我沒有發現這是非常可靠的。