I am trying to create an application that connects and receives notifications from multiple bluetooth low energy devices. I am wondering how this can be achieved. Do I need a separate thread for each connection? How can I make sure the services get discovered and notifications get set in an order that works given the asynchronous nature of the API. I am currently using the same structure provided here: https://developer.android.com/guide/topics/connectivity/bluetooth-le.html. This is setup for a single connection only. Would I be able to keep this structure i.e. extending the Service class in the BluetoothLeService class and binding to the service. I have recently discovered that The Service class is a singleton so how would I go about creating different instances of my BluetootLeService class and receiving broadcast and registering the Broadcast Receiver/Receivers to handle changes from the appropriate devices.
3 Answers
I am wondering how this can be achieved
To achieve multiple BLE connection you have to store multiple BluetoothGatt
object and use those object for different device. To store multiple connection object of BluetoothGatt
you can use Map<>
private Map<String, BluetoothGatt> connectedDeviceMap;
On Service onCreate
initialize the Map
connectedDeviceMap = new HashMap<String, BluetoothGatt>();
Then before calling device.connectGatt(this, false, mGattCallbacks);
to connect to GATT Server check that device is already connected.
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(deviceAddress);
int connectionState = mBluetoothManager.getConnectionState(device, BluetoothProfile.GATT);
if(connectionState == BluetoothProfile.STATE_DISCONNECTED ){
// connect your device
device.connectGatt(this, false, mGattCallbacks);
}else if( connectionState == BluetoothProfile.STATE_CONNECTED ){
// already connected . send Broadcast if needed
}
On BluetoothGattCallback
if connection state is CONNECTED then store BluetoothGatt
object on Map
and if connection state is DISCONNECTED then remove it form Map
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
BluetoothDevice device = gatt.getDevice();
String address = device.getAddress();
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i(TAG, "Connected to GATT server.");
if (!connectedDeviceMap.containsKey(address)) {
connectedDeviceMap.put(address, gatt);
}
// Broadcast if needed
Log.i(TAG, "Attempting to start service discovery:" +
gatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Disconnected from GATT server.");
if (connectedDeviceMap.containsKey(address)){
BluetoothGatt bluetoothGatt = connectedDeviceMap.get(address);
if( bluetoothGatt != null ){
bluetoothGatt.close();
bluetoothGatt = null;
}
connectedDeviceMap.remove(address);
}
// Broadcast if needed
}
}
Similarly onServicesDiscovered(BluetoothGatt gatt, int status)
method you have BluetoothGatt
connection object on parameter and you can get device from that BluetoothGatt
. And other callback method like public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
you will get the device form gatt
.
When you need to writeCharacteristic or writeDescriptor, get BluetoothGatt
object from Map
and use that BluetoothGatt
object to call gatt.writeCharacteristic(characteristic)
gatt.writeDescriptor(descriptor)
for different connection.
Do I need a separate thread for each connection?
I think you don't need to use separate Thread for each connection. Just run the Service
on a Background Thread.
Hope this help you.

- 5,729
- 3
- 31
- 50
-
Are you doing this all in one class?. The current structure I have now is where the callbacks and actually connecting are done in another class i.e. DeviceControlActivity has instances of BluetoohLeService. If so should i just make the hashmap in DeviceControlActivity and each instance of BlueToothLeService has a separate bluetoohgatt. – IceManStan Nov 21 '17 at 03:58
-
1Yes i have done all this code on `Service`. You can use `BluetoothGattCallback` and `Map` in Service. `Map` is just for store separate `BluetoothGattCallback` object. If you need you can share `Map` between classes. Android `Service` doesn't have many instance. If a `Service` is running and you start that Service` again system will not start `Service` again it will just call `onStartCommand()`. – Abu Yousuf Nov 21 '17 at 04:16
-
Ok so what your saying is to store the hashmap in the Service and that single instance of the Service can handle all the devices. When sending a broadcast to update my activity could I differentiate between the devices maybe via the intent. – IceManStan Nov 21 '17 at 05:30
-
send device address to Broadcast Intent – Abu Yousuf Nov 21 '17 at 05:35
-
Ok I will try these suggestions out. Thank you for your replies :) btw my current reputation on this site prohibits me from upvoting your answer(at least it does not show on here even though it said it was recorded) – IceManStan Nov 21 '17 at 05:36
-
If you think my answer solved your problem or helps you feel free to accept and up vote :p – Abu Yousuf Nov 21 '17 at 05:40
-
Once I connect to multiple devices how do i set notifications for all of them. Do i need to put a delay between all readings? – IceManStan Nov 27 '17 at 21:51
-
To set notification get `gatt` from map and use that `gatt` to set notification. I think no delay required for reading data. – Abu Yousuf Nov 28 '17 at 03:11
-
Brilliant idea having a map of BluetoothGatt objects! Thanks – luckyging3r Mar 19 '19 at 21:41
-
With this method. On every disconnection and reconnecton, there are multiple gatt callback called with there notification received multiple times. How do I resolve that? – Ashutosh Soni Sep 09 '20 at 19:45
-
@AbuYousuf if I can ask you some questions about multiple devices with one Android app (connecting at different times) - should I define multiple UUID in android studio? Can you help :(? – swiftlearneer Oct 27 '21 at 03:59
Abu Yousuf's answer really helped me, also because I couldn't find anything similiar on the Internet. I'd like to add one thing I struggled with: Better not save your BluetoothGattCharacteristic in a global variable because it is unique and different for every connected device. So rather retrieve it in every action, e.g. when you want to write a new value:
BluetoothGatt gatt = connectedDeviceMap.get(address);
BluetoothGattCharacteristic localChar = gatt.getService(SERVICE_UUID).getCharacteristic(CHAR_UUID);
localChar.setValue(value);
gatt.writeCharacteristic(localChar);

- 31
- 2
I saw the answer from the Gatt Client (Central) side. But the solution to this problem is even easier if you're on the peripheral side and need to connect multiple central.
Just retain their addresses in a arrayListOf
as mentioned below:
var connectedDevices = arrayListOf<BluetoothDevice>()
and when notifying about changes of characteristics (sending), just point out to the particular device you want to send it to:
fun send(content: String, deviceIndex: Int) {
characteristic.setValue(content)
Log.i("NOTIFY",
"notifyCharacteristicChanged called: ${
gattServer.notifyCharacteristicChanged(connectedDevicesArray[deviceIndex],
characteristics,
true)}")
}

- 322
- 3
- 5