55

Android Bluetooth Low Energy API implements 1 method to connect to the device connectGatt() but 2 methods to close the connection disconnect() and close().

Documentation says:

  • disconnect(): Disconnects an established connection, or cancels a connection attempt currently in progress.

  • close(): Application should call this method as early as possible after it is done with this GATT client.

The source code of BluetoothGatt.java shows that close() unregisters the application and disconnect() disconnect the client. However it does not say what that actually means. I mean, if there is only 1 way to connect to the client, why there are 2 ways to close/disconnect the connection?

Chisko
  • 3,092
  • 6
  • 27
  • 45
Arturs Vancans
  • 4,531
  • 14
  • 47
  • 76

2 Answers2

94

With disconnect() you can later call connect() and continue with that cycle.

Once you call close() you are done. If you want to connect again you will have to call connectGatt() on the BluetoothDevice again; close() will release any resources held by BluetoothGatt.

Douglas Jones
  • 2,522
  • 1
  • 23
  • 27
  • 4
    Super helpful- was only calling `disconnect()` and kept running out of BT interfaces preventing any new connections. Calling `close()` prevents this. – dgel Sep 11 '14 at 21:42
  • 10
    Whenever I use `disconnect()` or `close()` the callback method `onConnectionStateChange` never get called indicating STATE_DISCONNECTED. – DaviF Dec 08 '14 at 14:31
  • 2
    close() won't get a call to onConnectionStateChange, but disconnect() should. Are you sure you were really connected when you called disconnect()? – Douglas Jones Dec 08 '14 at 17:58
  • Can I call `close()`, without calling `disconnect()`? And will `onConnectionStateChange` be called in the case? – Alexander Farber Mar 20 '15 at 13:37
  • After calling `disconnect()` and `close()` the `onCharacteristicChanged()` event keeps firing. Does anybody have a solution for this? – Brabbeldas Jun 23 '15 at 07:13
  • 2
    @AlexanderFarber you can call close() without disconnect() but you won't get an onConnectionStateChange callback in my experience. – Douglas Jones Jun 23 '15 at 14:09
  • @Brabbeldas My thought is that you probably have multiple peripherals connected and are getting callbacks for the other peripherals, or you disconnected and closed not what you thought you did. – Douglas Jones Jun 23 '15 at 14:12
  • @DouglasJones Thanks for replying. I am sure I only have a single peripheral connected. I am also sure I only call `mBluetoothGatt = device.connectGatt()` a single time. – Brabbeldas Jun 23 '15 at 14:17
  • So far, I've only used `close()` instead of both. But, I've just discovered on a Nexus 6P running Android 7.0, that if I call `close()` on a device that is currently pending to be connected, but not connected yet, the smartphone still reconnects to the device as soon as it becomes visible again... – Linard Arquint Oct 03 '16 at 08:45
  • `disconnect()` does not result in an `onConnectionStateChange()` if the connection had not already been established. – Alix Aug 31 '17 at 12:54
  • 2
    I have also struggled with inconsistent behaviors. If you call gatt.close(), the next time you connect to the same device, you will need to redo service discovery.Bonded BTLE devices are not supposed to do that.A bonded peer knows who you are (both for pairing and whether or not characteristics have been enabled) and can (by spec) send any data immediately. It will assume that you know its services. If you have done a gatt close, you wont know its services and may lose that data. I do not know what Android does down low, but working with embedded low level stacks this is definitely the case. – Brian Reinhold Mar 28 '19 at 10:03
15

Here is some food for thought:

As long as you have not called close on the Gatt, you can still try to connect to it, or discover. So when I try to discover services for a machine, I will usually run a thread or runnable that makes the request to connect to the machine for a certain period of time.

The first attempt with a machine connection, will return a BluetoothGatt object that you can later use to try to discover the services for the BluetoothDevice object. It seems pretty easy to connect, but much harder to discover the machine servces.

mBluetoothGatt = machine.getDevice().connectGatt(this, false, mGattCallback);

So in my thread / runnable, I will check to see if the BluetoothGatt is null. If it is, I will call the above line of code again, else I will attempt to discover the BluetoothGatt services as such.

mBluetoothGatt.discoverServices();

Oh, and I ALWAYS make sure to call BluetoothAdapter.cancelDiscovery() before any attempt at connecting of discovering the service.

mBluetoothAdapter.cancelDiscovery();

Here is a method is use to connect in my runnable etc:

public void connectToMachineService(BLEMachine machine) {
    Log.i(SERVICE_NAME, "ATTEMPTING TO CONNECT TO machine.getDevice().getName());

    mBluetoothAdapter.cancelDiscovery();

    if(mBluetoothGatt == null)
        mBluetoothGatt = machine.getDevice().connectGatt(this, false, mGattCallback);
    else
        mBluetoothGatt.discoverServices();
}

Lastly, make sure that you close out any BluetoothGatt objects that you have connected to. It appears that Android can handle five BluetoothGatt objects before it starts saying "unable to connect to Gatt server" or other something like that.

On every BluetoothGatt that I create, I will call close on it then broadcast an update stating the connection is closed. It seems that there are a lot of times where the BluetootGatt will not respond with a state change when it becomes disconnected. My method of closing the BluetoothGatt goes something like this. I leave the method open up for the Activity to call the service and disconnect if a machine becomes non - responsive and the disconnect state is not called.

public void disconnectGatt(BluetoothGatt gatt) {
    if(gatt != null) {
        gatt.close();
        gatt = null;
    }

    broadcastUpdate(ACTION_STATE_CLOSED);
}
Droid Chris
  • 3,455
  • 28
  • 31
  • 4
    It is a little bit confusing that you call the method `disconnectGatt` when you actually call `close()` instead of `disconnect()`. – Mr. Blond May 27 '19 at 18:03