flutter_reactive_ble icon indicating copy to clipboard operation
flutter_reactive_ble copied to clipboard

Disconnect and Reconnect to device using Notification causes multiple instances of same device

Open mneber opened this issue 3 years ago • 5 comments

This issue happens after connecting, enabling notifications, then disabling notifications and disconnecting. When reconnecting to the same device, it looks like there are multiple instances of the same device connected. See the screenshot below where I connect and disconnect to the same device three times.

Screen Shot 2021-05-01 at 7 17 12 PM

I am turning off notifications and awaiting a timer before disconnecting, but this has no effect:

class BleDeviceConnector extends ReactiveState<ConnectionStateUpdate> {
  BleDeviceConnector(this._ble);

  final FlutterReactiveBle _ble;
  final _deviceConnection = StreamController<ConnectionStateUpdate>.broadcast();

  @override
  Stream<ConnectionStateUpdate> get state => _deviceConnection.stream;

  StreamSubscription<ConnectionStateUpdate> _connection;
  StreamSubscription<List<int>> _notifications;

  Future<void> connect(String deviceId) async {
    _deviceConnection.add(ConnectionStateUpdate(
        deviceId: deviceId,
        connectionState: DeviceConnectionState.connecting,
        failure: null));

    if (_connection != null) {
      await _connection.cancel();
    }
    _connection = _ble
        .connectToDevice(id: deviceId)
        .listen((update) => _deviceConnection.add(update));
  }

  Future<StreamSubscription<List<int>>> startNotification(
      QualifiedCharacteristic characteristic) async {
    if (_notifications != null) {
      await _notifications.cancel();
    }
    _notifications =
        _ble.subscribeToCharacteristic(characteristic).listen((data) {},
            onDone: () => print('stream event done'),
            onError: (dynamic error) {
              print('Start Notification returns Error: $error');
            });
    return _notifications;
  }

  Future<void> disconnect(String deviceId) async {
    _deviceConnection.add(ConnectionStateUpdate(
        deviceId: deviceId,
        connectionState: DeviceConnectionState.disconnecting,
        failure: null));
    if (_connection != null) {
      try {
        if (_notifications != null) {
          await _notifications.cancel().whenComplete(() async {
            _notifications = null;
            await Future.delayed(Duration(milliseconds: 5000));
          });
        }
        await _connection.cancel().whenComplete(() {
          _connection = null;
        });
      } on Exception catch (e, _) {
        print("Error disconnecting from a device: $e");
      } finally {
        // Since [_connection] subscription is terminated, the "disconnected" state cannot be received and propagated
        _deviceConnection.add(
          ConnectionStateUpdate(
            deviceId: deviceId,
            connectionState: DeviceConnectionState.disconnected,
            failure: null,
          ),
        );
      }
    }
  }

  Future<void> discoverServices(String deviceId) async {
    await _ble.discoverServices(deviceId).then(
          (value) => print('Services discovered: $value'),
        );
  }

  Future<void> dispose() async {
    await _deviceConnection.close();
  }
}

Is there something I may be missing as far as closing out stream subscriptions?

mneber avatar May 01 '21 23:05 mneber

Thanks for reporting! I can reproduce this behavior on the example app looks like something is going wrong.

remonh87 avatar May 02 '21 14:05 remonh87

Are there any updates on this? I am experiencing the same behaviour

nicolosponziello avatar Jul 13 '21 11:07 nicolosponziello

I think this issue may be related to the issue I am experiencing: When I disconnect from a peripheral i cannot instantly reconnect to it, it takes about 15-20 seconds to work again. But if I close the app completely and reopen it it works in an instant.

skast96 avatar Mar 03 '22 09:03 skast96

is there anything new here? unfortunately, i am observing the same behavior right now.

andrepura avatar Jun 29 '23 13:06 andrepura

looks like the problem is:

flutterReactiveBle
        .connectToAdvertisingDevice(
            id: discoveredDevice.id,
            withServices: <Uuid>[deviceInformationService],
            prescanDuration: scanAndConnectionTime,
            connectionTimeout: scanAndConnectionTime)

Returns a stream. Cancel the stream does not disconnect from device (see also: #597 ). so it is necessary to reuse the subscription for the same device, otherwise there will be a second subscription to the device and you will receive all events multiple times.

andrepura avatar Jun 29 '23 14:06 andrepura