dotintent / FlutterBleLib

Bluetooth Low Energy library for Flutter with support for simulating peripherals
Apache License 2.0
535 stars 197 forks source link

Plugin leaks receivers upon connection attempts #528

Closed b055man closed 3 years ago

b055man commented 3 years ago

Observed on version: 2.2.9

After leaving the application reconnecting to a gone device for several hours, the app is no longer operational - it is not possible to register any receivers as app hit the limit of 1000 registered ones.

When it happened once, I later tried to pinpoint the culprit (wasnt't sure if this may be scanning related, or maybe even Wifi portion of the app's functionality) and I reproduced it quickly by repeatedly initiating connections. Snippet from the code handling the connection:

Log.i(
        'Trying to connect... : ${_peripheral.identifier}, attempt: $retryNum');
    bool connected = false;
    Stopwatch stopwatch = Stopwatch()..start();
    try {final isConnected = await _peripheral.isConnected();
      if (!isConnected) {
        await _peripheral
            .connect(isAutoConnect: autoConnect, refreshGatt: true)
            .timeout(autoConnect ? timeoutAuto : timeoutDirect, onTimeout: () {
          Log.i('Timeout when attempting to connect ${_peripheral.identifier}');
          _peripheral.disconnectOrCancelConnection();
          throw TimeoutException(
              'Failed to connect ${_peripheral.identifier} on time');
        });
      }

Once 1000 connections were reached, this is what I got:

09-17 00:25:20.871  4526  4526 I Fimber.i: [BleDeviceBloc._connect]:   Trying to connect... : 00:1D:96:07:C1:1D, attempt: 1000
09-17 00:25:20.878  4526  4526 D com.polidea.flutter_ble_lib.FlutterBleLibPlugin: on native side observed method: isDeviceConnected
09-17 00:25:20.895  4526  4878 D RxBle#ClientOperationQueue: FINISHED DisconnectOperation(261771749) in 81 ms
09-17 00:25:20.918  4526  4526 D com.polidea.flutter_ble_lib.FlutterBleLibPlugin: on native side observed method: connectToDevice
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib: Failed to handle method call
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib: rx.exceptions.OnErrorNotImplementedException: Too many receivers, total of 1000, registered for pid: 4526, callerPackage: com.*****
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java:386)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java:383)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.util.ActionSubscriber.onError(ActionSubscriber.java:44)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:153)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:115)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.NotificationLite.accept(NotificationLite.java:132)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.CachedObservable$ReplayProducer.replay(CachedObservable.java:403)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.CachedObservable$CacheState.dispatch(CachedObservable.java:220)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.CachedObservable$CacheState.onError(CachedObservable.java:201)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.CachedObservable$CacheState$1.onError(CachedObservable.java:175)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OperatorSingle$ParentSubscriber.onError(OperatorSingle.java:129)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OperatorTake$1.onError(OperatorTake.java:66)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OperatorMerge$MergeSubscriber.reportError(OperatorMerge.java:266)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OperatorMerge$MergeSubscriber.checkTerminate(OperatorMerge.java:818)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OperatorMerge$MergeSubscriber.emitLoop(OperatorMerge.java:579)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OperatorMerge$MergeSubscriber.emit(OperatorMerge.java:568)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OperatorMerge$InnerSubscriber.onError(OperatorMerge.java:855)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeMap$MapSubscriber.onError(OnSubscribeMap.java:88)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeFilter$FilterSubscriber.onError(OnSubscribeFilter.java:90)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.observers.SerializedObserver.onError(SerializedObserver.java:152)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.observers.SerializedSubscriber.onError(SerializedSubscriber.java:78)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.innerError(OnSubscribeConcatMap.java:192)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeConcatMap$ConcatMapInnerSubscriber.onError(OnSubscribeConcatMap.java:340)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeMap$MapSubscriber.onError(OnSubscribeMap.java:88)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.unsafeSubscribe(Observable.java:10334)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.unsafeSubscribe(Observable.java:10327)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.drain(OnSubscribeConcatMap.java:286)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.onNext(OnSubscribeConcatMap.java:144)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeFromArray$FromArrayProducer.slowPath(OnSubscribeFromArray.java:100)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeFromArray$FromArrayProducer.request(OnSubscribeFromArray.java:63)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Subscriber.setProducer(Subscriber.java:211)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeFromArray.call(OnSubscribeFromArray.java:32)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeFromArray.call(OnSubscribeFromArray.java:24)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.unsafeSubscribe(Observable.java:10327)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:94)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:42)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.unsafeSubscribe(Observable.java:10327)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeFilter.call(OnSubscribeFilter.java:45)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeFilter.call(OnSubscribeFilter.java:30)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.unsafeSubscribe(Observable.java:10327)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.unsafeSubscribe(Observable.java:10327)
09-17 00:25:21.165  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OperatorMerge$MergeSubscriber.onNext(OperatorMerge.java:248)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OperatorMerge$MergeSubscriber.onNext(OperatorMerge.java:148)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeFromArray$FromArrayProducer.fastPath(OnSubscribeFromArray.java:76)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeFromArray$FromArrayProducer.request(OnSubscribeFromArray.java:58)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Subscriber.setProducer(Subscriber.java:211)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeFromArray.call(OnSubscribeFromArray.java:32)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeFromArray.call(OnSubscribeFromArray.java:24)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.unsafeSubscribe(Observable.java:10327)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.CachedObservable$CacheState.connect(CachedObservable.java:183)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.CachedObservable$CachedSubscribe.call(CachedObservable.java:248)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.CachedObservable$CachedSubscribe.call(CachedObservable.java:230)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.subscribe(Observable.java:10423)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.subscribe(Observable.java:10390)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.subscribe(Observable.java:10165)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.rxandroidble.internal.connection.DisconnectionRouter.<init>(DisconnectionRouter.java:70)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.rxandroidble.internal.connection.DisconnectionRouter_Factory.get(DisconnectionRouter_Factory.java:33)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.rxandroidble.internal.connection.DisconnectionRouter_Factory.get(DisconnectionRouter_Factory.java:10)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at bleshadow.dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.rxandroidble.internal.connection.RxBleGattCallback_Factory.get(RxBleGattCallback_Factory.java:33)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.rxandroidble.internal.connection.RxBleGattCallback_Factory.get(RxBleGattCallback_Factory.java:8)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at bleshadow.dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.rxandroidble.DaggerClientComponent$DeviceComponentImpl$ConnectionComponentImpl.connectOperation(DaggerClientComponent.java:713)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.rxandroidble.internal.connection.ConnectorImpl$1.call(ConnectorImpl.java:54)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.rxandroidble.internal.connection.ConnectorImpl$1.call(ConnectorImpl.java:38)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.unsafeSubscribe(Observable.java:10327)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.unsafeSubscribe(Observable.java:10327)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.subscribe(Observable.java:10423)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.subscribe(Observable.java:10390)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.subscribe(Observable.java:10298)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.multiplatformbleadapter.BleModule.safeConnectToDevice(BleModule.java:1364)
09-17 00:25:21.219  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.multiplatformbleadapter.BleModule.connectToDevice(BleModule.java:437)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.flutter_ble_lib.delegate.DeviceConnectionDelegate.connectToDevice(DeviceConnectionDelegate.java:106)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.flutter_ble_lib.delegate.DeviceConnectionDelegate.onMethodCall(DeviceConnectionDelegate.java:50)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.flutter_ble_lib.FlutterBleLibPlugin.onMethodCall(FlutterBleLibPlugin.java:98)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:230)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:692)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.os.MessageQueue.nativePollOnce(Native Method)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.os.MessageQueue.next(MessageQueue.java:326)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.os.Looper.loop(Looper.java:170)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.app.ActivityThread.main(ActivityThread.java:6991)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at java.lang.reflect.Method.invoke(Native Method)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib: Caused by: java.lang.IllegalStateException: Too many receivers, total of 1000, registered for pid: 4526, callerPackage: com.motorolasolutions.camera.smartcontrol.debug
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.os.Parcel.createException(Parcel.java:1958)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.os.Parcel.readException(Parcel.java:1918)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.os.Parcel.readException(Parcel.java:1868)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.app.IActivityManager$Stub$Proxy.registerReceiver(IActivityManager.java:3705)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1515)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.app.ContextImpl.registerReceiver(ContextImpl.java:1476)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.app.ContextImpl.registerReceiver(ContextImpl.java:1464)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:623)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.rxandroidble.RxBleAdapterStateObservable$1.call(RxBleAdapterStateObservable.java:61)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at com.polidea.rxandroidble.RxBleAdapterStateObservable$1.call(RxBleAdapterStateObservable.java:47)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeCreate.call(OnSubscribeCreate.java:72)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.internal.operators.OnSubscribeCreate.call(OnSubscribeCreate.java:32)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at rx.Observable.unsafeSubscribe(Observable.java:10327)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     ... 84 more
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib: Caused by: android.os.RemoteException: Remote stack trace:
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at com.android.server.am.ActivityManagerService.registerReceiver(ActivityManagerService.java:22002)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.app.IActivityManager$Stub.onTransact$registerReceiver$(IActivityManager.java:10441)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:154)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3548)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib:     at com.android.server.am.ActivityManagerServiceEx.onTransact(ActivityManagerServiceEx.java:476)
09-17 00:25:21.298  4526  4526 E MethodChannel#flutter_ble_lib: 
09-17 00:25:21.313  4526  4526 I Fimber.i: [BleDeviceBloc._connect.<anonymous]:   Timeout when attempting to connect 00:1D:96:07:C1:1D

With a profiler from Android Studio I am indeed able to see that there are things that are not released and clearly point to flutter_ble_lib - or rather rxandroidble

image image

Once that exception is hit, subsequent calls to connect method end instantly with the following error:

BleError (Error code: 203, ATT error code: null, iOS error code: null, Android error code: null, reason: Already connected to device with MAC address 00:1D:96:07:C1:1D, internal message: null, device ID: null, service UUID: null, characteristic UUID: null, descriptor UUID: null)

There doesn't seem to be a way of recovering from this - destroying the client does not help. Only way to resolve that seems to require swiping out the app from the recents application list.

I noticed an issue about a leak in the rxandroidble but it was related to scanning (https://github.com/Polidea/RxAndroidBle/issues/607) - this one seems to be a bigger issue.

Any hints are appreciated - this is a huge deal for the app I'm working on.

b055man commented 3 years ago

@dariuszseweryn the stacktrace from AndroidStudio seems to point directly at the place in question.. https://github.com/Polidea/RxAndroidBle/blob/8550bd66493c8e12449c784ca28ed0b167e67fc3/rxandroidble/src/main/java/com/polidea/rxandroidble2/RxBleAdapterStateObservable.java#L61

dariuszseweryn commented 3 years ago

Hi @b055man Thanks for the report — could you create a minimal project that reproduces the issue?

b055man commented 3 years ago

@dariuszseweryn Just tried that using a newly created Flutter example as base with flutter_ble_lib 2.3.0, Flutter: 1.20.3

main.dart

import 'package:ble_lib_leak/connect_ble.dart';
import 'package:flutter/material.dart';
import 'package:flutter_ble_lib/flutter_ble_lib.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  TextEditingController _controller;
  bool _isConnecting = false;

  @override
  void initState() {
    _controller = TextEditingController();
    _controller.value = TextEditingValue(text: '0A:1D:96:07:C1:1D');
    super.initState();
  }

  @override
  void dispose() {
    BleManager().destroyClient();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            TextField(
              controller: _controller,
            ),
            FlatButton(
              child: Text('Start connecting'),
              onPressed: _isConnecting
                  ? null
                  : () {
                      setState(() {
                        _isConnecting = true;
                      });
                      print(_controller.value.text);
                      initiateBurstConnecting(_controller.value.text).then((_) => 
                        _isConnecting = false);
                    },
            )
          ],
        ),
      ),
    );
  }
}

connect_ble.dart

import 'package:flutter_ble_lib/flutter_ble_lib.dart';

Future initiateBurstConnecting(String id) async {
  final bleManager = BleManager();
  await bleManager.createClient();
  await bleManager.setLogLevel(LogLevel.verbose);
  return _connect(bleManager.createUnsafePeripheral(id));
}

Future _connect(Peripheral peripheral, {int retryNum = 1}) async {
  print('Trying to connect... : ${peripheral.identifier}, attempt: $retryNum');
  bool connected = false;
  final isConnected = await peripheral.isConnected();
  if (!isConnected) {
    try {
      await peripheral.connect(
          isAutoConnect: retryNum % 2 == 1,
          refreshGatt: true,
          timeout: Duration(milliseconds: 100));
      connected = await peripheral.isConnected();
      print('Device ${peripheral.identifier} connection result: $connected');
    } catch (e) {
      print('Detailed cause: $e');
      connected = false;
    }
  }
  print(
      'Finished connecting to ${peripheral.identifier}: $connected, retry: $retryNum}');
  return connected || await _connect(peripheral, retryNum: ++retryNum);
}

Press 'start connecting' and wait a bit. You'll get:

D/RxBle#ClientOperationQueue(28933): FINISHED DisconnectOperation(132979214) in 24 ms
E/MethodChannel#flutter_ble_lib(28933): Failed to handle method call
E/MethodChannel#flutter_ble_lib(28933): rx.exceptions.OnErrorNotImplementedException: Too many receivers, total of 1000, registered for pid: 28933, callerPackage: com.example.ble_lib_leak
E/MethodChannel#flutter_ble_lib(28933):     at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java:386)
dariuszseweryn commented 3 years ago

If you could create a repository which I could clone, build and recreate the issue — that would be perfect

b055man commented 3 years ago

@dariuszseweryn Ok, here you go: https://github.com/b055man/ble_lib_leak

b055man commented 3 years ago

@dariuszseweryn were you able to reproduce the issue maybe?

b055man commented 3 years ago

@mikolak @dariuszseweryn Can I be of any help with addressing this issue?

Thanks, A.

dariuszseweryn commented 3 years ago

Hello @b055man I am recently pretty much saturated with amount of work. Yes, you could help — what we need to do is to perform the test and trace down leaked BroadcastReceivers. It should be fairly simple using LeakCanary — it would probably need a minor native app change to integrate.

b055man commented 3 years ago

@dariuszseweryn

I'm not sure if LeakCanary is good in tracing leaked Broadcast receivers.. Anyway, I can see a leak warning (pointing to RxBleAdapterStateObservable) if I do even a single connection attempt and trigger the heap dump (simply by leaving the app/putting it in the background). What's also interesting is that if I destroy the BLE client after all connection attempts, there are no leak warnings, yet the receivers are still leaked - the app will crash upon hitting the 1000th connection attempt.

Here's the output from LeakCanary - I hope it can be helpful. ``` Launching lib/main.dart on H8324 in debug mode... ✓ Built build/app/outputs/flutter-apk/app-debug.apk. Installing build/app/outputs/flutter-apk/app.apk... Connecting to VM Service at ws://127.0.0.1:64842/A5eQn6v56E0=/ws D/LeakCanary( 3071): Check for retained object found no objects remaining W/Gralloc3( 3071): mapper 3.x is not supported I/flutter ( 3071): 0A:1D:96:07:C1:1D D/com.polidea.flutter_ble_lib.FlutterBleLibPlugin( 3071): on native side observed method: createClient I/flutter ( 3071): set log level to verbose D/com.polidea.flutter_ble_lib.FlutterBleLibPlugin( 3071): on native side observed method: setLogLevel D/com.polidea.flutter_ble_lib.delegate.LogLevelDelegate( 3071): set log level to: verbose I/flutter ( 3071): Trying to connect... : 0A:1D:96:07:C1:1D, attempt: 1 D/com.polidea.flutter_ble_lib.FlutterBleLibPlugin( 3071): on native side observed method: isDeviceConnected D/com.polidea.flutter_ble_lib.FlutterBleLibPlugin( 3071): on native side observed method: connectToDevice W/le.ble_lib_lea( 3071): Accessing hidden method Lsun/misc/Unsafe;->objectFieldOffset(Ljava/lang/reflect/Field;)J (greylist,core-platform-api, linking, allowed) W/le.ble_lib_lea( 3071): Accessing hidden method Lsun/misc/Unsafe;->compareAndSwapInt(Ljava/lang/Object;JII)Z (greylist, linking, allowed) W/le.ble_lib_lea( 3071): Accessing hidden method Lsun/misc/Unsafe;->getIntVolatile(Ljava/lang/Object;J)I (greylist, linking, allowed) W/le.ble_lib_lea( 3071): Accessing hidden field Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe; (greylist, reflection, allowed) W/le.ble_lib_lea( 3071): Accessing hidden method Lsun/misc/Unsafe;->arrayIndexScale(Ljava/lang/Class;)I (greylist, linking, allowed) W/le.ble_lib_lea( 3071): Accessing hidden method Lsun/misc/Unsafe;->arrayBaseOffset(Ljava/lang/Class;)I (greylist,core-platform-api, linking, allowed) W/le.ble_lib_lea( 3071): Accessing hidden method Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object; (greylist, linking, allowed) W/le.ble_lib_lea( 3071): Accessing hidden method Lsun/misc/Unsafe;->getObjectVolatile(Ljava/lang/Object;J)Ljava/lang/Object; (greylist, linking, allowed) W/le.ble_lib_lea( 3071): Accessing hidden method Lsun/misc/Unsafe;->putOrderedObject(Ljava/lang/Object;JLjava/lang/Object;)V (greylist, linking, allowed) W/le.ble_lib_lea( 3071): Accessing hidden method Lsun/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V (greylist, linking, allowed) W/le.ble_lib_lea( 3071): Accessing hidden method Lsun/misc/Unsafe;->getLongVolatile(Ljava/lang/Object;J)J (greylist, linking, allowed) W/le.ble_lib_lea( 3071): Accessing hidden method Lsun/misc/Unsafe;->putOrderedLong(Ljava/lang/Object;JJ)V (greylist, linking, allowed) D/RxBle#ClientOperationQueue( 3071): QUEUED ConnectOperation(31049058) W/le.ble_lib_lea( 3071): Accessing hidden method Lsun/misc/Unsafe;->getObjectVolatile(Ljava/lang/Object;J)Ljava/lang/Object; (greylist, linking, allowed) D/RxBle#ClientOperationQueue( 3071): STARTED ConnectOperation(31049058) V/RxBle#BleConnectionCompat( 3071): Connecting without reflection D/BluetoothGatt( 3071): connect() - device: 0A:1D:96:07:C1:1D, auto: true D/BluetoothGatt( 3071): registerApp() D/BluetoothGatt( 3071): registerApp() - UUID=fc57a617-b6eb-4d40-8039-0f26d99f09c6 D/RxBle#ClientOperationQueue( 3071): FINISHED ConnectOperation(31049058) in 8 ms D/BluetoothGatt( 3071): onClientRegistered() - status=0 clientIf=11 D/RxBle#ClientOperationQueue( 3071): QUEUED DisconnectOperation(12230330) I/RxBle#ConnectionOperationQueue( 3071): Connection operations queue to be terminated (0A:1D:96:07:C1:1D) D/RxBle#ClientOperationQueue( 3071): STARTED DisconnectOperation(12230330) D/RxBle#Executors$RunnableAdapter( 3071): Terminated. D/BluetoothGatt( 3071): close() D/BluetoothGatt( 3071): unregisterApp() - mClientIf=11 D/RxBle#ClientOperationQueue( 3071): FINISHED DisconnectOperation(12230330) in 5 ms I/flutter ( 3071): Detailed cause: BleError (Error code: 2, ATT error code: null, iOS error code: null, Android error code: null, reason: null, internal message: null, device ID: null, service UUID: null, characteristic UUID: null, descriptor UUID: null) I/flutter ( 3071): Finished connecting to 0A:1D:96:07:C1:1D: false, retry: 1} D/LeakCanary( 3071): Scheduling check for retained objects in 5000ms because app became invisible D/LeakCanary( 3071): Watching instance of com.example.ble_lib_leak.MainActivity (com.example.ble_lib_leak.MainActivity received Activity#onDestroy() callback) with key c43c18c7-3f22-44c8-b4ae-d5dce58c5373 D/LeakCanary( 3071): Ignoring request to check for retained objects (found new object retained), already scheduled in -20ms I/le.ble_lib_lea( 3071): Explicit concurrent copying GC freed 399(60KB) AllocSpace objects, 0(0B) LOS objects, 24% free, 1908KB/2545KB, paused 66us total 27.894ms D/LeakCanary( 3071): Check for retained objects found 1 objects, dumping the heap D/LeakCanary( 3071): WRITE_EXTERNAL_STORAGE permission not granted, ignoring I/le.ble_lib_lea( 3071): hprof: heap dump "/data/user/0/com.example.ble_lib_leak/files/leakcanary/2020-09-30_22-35-03_352.hprof" starting... I/le.ble_lib_lea( 3071): hprof: heap dump completed (17MB) in 3.048s objects 240078 objects with stack traces 0 D/LeakCanary( 3071): Analysis in progress, working on: PARSING_HEAP_DUMP D/LeakCanary( 3071): Analysis in progress, working on: EXTRACTING_METADATA D/LeakCanary( 3071): Analysis in progress, working on: FINDING_RETAINED_OBJECTS D/LeakCanary( 3071): Analysis in progress, working on: FINDING_PATHS_TO_RETAINED_OBJECTS D/LeakCanary( 3071): Setting up flushing for Thread[IntentService[HeapAnalyzerService],5,main] D/LeakCanary( 3071): Analysis in progress, working on: FINDING_DOMINATORS D/LeakCanary( 3071): Found 1 retained objects D/LeakCanary( 3071): Analysis in progress, working on: COMPUTING_NATIVE_RETAINED_SIZE D/LeakCanary( 3071): Analysis in progress, working on: COMPUTING_RETAINED_SIZE D/LeakCanary( 3071): Analysis in progress, working on: BUILDING_LEAK_TRACES D/LeakCanary( 3071): Found 1 paths to retained objects D/LeakCanary( 3071): Analysis in progress, working on: REPORTING_HEAP_ANALYSIS D/LeakCanary( 3071): ==================================== D/LeakCanary( 3071): HEAP ANALYSIS RESULT D/LeakCanary( 3071): ==================================== D/LeakCanary( 3071): 1 APPLICATION LEAKS D/LeakCanary( 3071): D/LeakCanary( 3071): References underlined with "~~~" are likely causes. D/LeakCanary( 3071): Learn more at https://squ.re/leaks. D/LeakCanary( 3071): D/LeakCanary( 3071): 47539 bytes retained by leaking objects D/LeakCanary( 3071): Signature: 80fbdbdf56e375ce1c0ca141253522c45c4dc D/LeakCanary( 3071): ┬─── D/LeakCanary( 3071): │ GC Root: System class D/LeakCanary( 3071): │ D/LeakCanary( 3071): ├─ android.provider.FontsContract class D/LeakCanary( 3071): │ Leaking: NO (FlutterApplication↓ is not leaking and a class is never leaking) D/LeakCanary( 3071): │ ↓ static FontsContract.sContext D/LeakCanary( 3071): ├─ io.flutter.app.FlutterApplication instance D/LeakCanary( 3071): │ Leaking: NO (Application is a singleton) D/LeakCanary( 3071): │ ↓ FlutterApplication.mLoadedApk D/LeakCanary( 3071): │ ~~~~~~~~~~ D/LeakCanary( 3071): ├─ android.app.LoadedApk instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ LoadedApk.mReceivers D/LeakCanary( 3071): │ ~~~~~~~~~~ D/LeakCanary( 3071): ├─ android.util.ArrayMap instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ ArrayMap.mArray D/LeakCanary( 3071): │ ~~~~~~ D/LeakCanary( 3071): ├─ java.lang.Object[] array D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ Object[].[1] D/LeakCanary( 3071): │ ~~~ D/LeakCanary( 3071): ├─ android.util.ArrayMap instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ ArrayMap.mArray D/LeakCanary( 3071): │ ~~~~~~ D/LeakCanary( 3071): ├─ java.lang.Object[] array D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ Object[].[0] D/LeakCanary( 3071): │ ~~~ D/LeakCanary( 3071): ├─ com.polidea.rxandroidble.RxBleAdapterStateObservable$1$1 instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ Anonymous subclass of android.content.BroadcastReceiver D/LeakCanary( 3071): │ ↓ RxBleAdapterStateObservable$1$1.val$emitter D/LeakCanary( 3071): │ ~~~~~~~~~~~ D/LeakCanary( 3071): ├─ rx.internal.operators.OnSubscribeCreate$LatestEmitter instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ OnSubscribeCreate$LatestEmitter.actual D/LeakCanary( 3071): │ ~~~~~~ D/LeakCanary( 3071): ├─ rx.internal.operators.OnSubscribeMap$MapSubscriber instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ OnSubscribeMap$MapSubscriber.mapper D/LeakCanary( 3071): │ ~~~~~~ D/LeakCanary( 3071): ├─ com.polidea.multiplatformbleadapter.BleModule$13 instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ Anonymous class implementing rx.functions.Func1 D/LeakCanary( 3071): │ ↓ BleModule$13.this$0 D/LeakCanary( 3071): │ ~~~~~~ D/LeakCanary( 3071): ├─ com.polidea.multiplatformbleadapter.BleModule instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ BleModule.connectingDevices D/LeakCanary( 3071): │ ~~~~~~~~~~~~~~~~~ D/LeakCanary( 3071): ├─ com.polidea.multiplatformbleadapter.utils.DisposableMap instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ DisposableMap.subscriptions D/LeakCanary( 3071): │ ~~~~~~~~~~~~~ D/LeakCanary( 3071): ├─ java.util.HashMap instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ HashMap.table D/LeakCanary( 3071): │ ~~~~~ D/LeakCanary( 3071): ├─ java.util.HashMap$Node[] array D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ HashMap$Node[].[0] D/LeakCanary( 3071): │ ~~~ D/LeakCanary( 3071): ├─ java.util.HashMap$Node instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ HashMap$Node.value D/LeakCanary( 3071): │ ~~~~~ D/LeakCanary( 3071): ├─ rx.observers.SafeSubscriber instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ SafeSubscriber.actual D/LeakCanary( 3071): │ ~~~~~~ D/LeakCanary( 3071): ├─ rx.internal.util.ObserverSubscriber instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ ObserverSubscriber.observer D/LeakCanary( 3071): │ ~~~~~~~~ D/LeakCanary( 3071): ├─ com.polidea.multiplatformbleadapter.BleModule$27 instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ Anonymous class implementing rx.Observer D/LeakCanary( 3071): │ ↓ BleModule$27.val$safeExecutor D/LeakCanary( 3071): │ ~~~~~~~~~~~~~~~~ D/LeakCanary( 3071): ├─ com.polidea.multiplatformbleadapter.utils.SafeExecutor instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ SafeExecutor.errorCallback D/LeakCanary( 3071): │ ~~~~~~~~~~~~~ D/LeakCanary( 3071): ├─ com.polidea.flutter_ble_lib.delegate.DeviceConnectionDelegate$5 instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ Anonymous class implementing com.polidea.multiplatformbleadapter.OnErrorCallback D/LeakCanary( 3071): │ ↓ DeviceConnectionDelegate$5.val$safeMainThreadResolver D/LeakCanary( 3071): │ ~~~~~~~~~~~~~~~~~~~~~~~~~~ D/LeakCanary( 3071): ├─ com.polidea.flutter_ble_lib.SafeMainThreadResolver instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ SafeMainThreadResolver.onErrorCallback D/LeakCanary( 3071): │ ~~~~~~~~~~~~~~~ D/LeakCanary( 3071): ├─ com.polidea.flutter_ble_lib.delegate.DeviceConnectionDelegate$2 instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ Anonymous class implementing com.polidea.multiplatformbleadapter.OnErrorCallback D/LeakCanary( 3071): │ ↓ DeviceConnectionDelegate$2.val$result D/LeakCanary( 3071): │ ~~~~~~~~~~ D/LeakCanary( 3071): ├─ io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1 instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ Anonymous class implementing io.flutter.plugin.common.MethodChannel$Result D/LeakCanary( 3071): │ ↓ MethodChannel$IncomingMethodCallHandler$1.val$reply D/LeakCanary( 3071): │ ~~~~~~~~~ D/LeakCanary( 3071): ├─ io.flutter.embedding.engine.dart.DartMessenger$Reply instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ DartMessenger$Reply.flutterJNI D/LeakCanary( 3071): │ ~~~~~~~~~~ D/LeakCanary( 3071): ├─ io.flutter.embedding.engine.FlutterJNI instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ FlutterJNI.localizationPlugin D/LeakCanary( 3071): │ ~~~~~~~~~~~~~~~~~~ D/LeakCanary( 3071): ├─ io.flutter.plugin.localization.LocalizationPlugin instance D/LeakCanary( 3071): │ Leaking: UNKNOWN D/LeakCanary( 3071): │ ↓ LocalizationPlugin.context D/LeakCanary( 3071): │ ~~~~~~~ D/LeakCanary( 3071): ╰→ com.example.ble_lib_leak.MainActivity instance D/LeakCanary( 3071): ​ Leaking: YES (ObjectWatcher was watching this because com.example.ble_lib_leak.MainActivity received Activity#onDestroy() callback and Activity#mDestroyed is true) D/LeakCanary( 3071): ​ key = c43c18c7-3f22-44c8-b4ae-d5dce58c5373 D/LeakCanary( 3071): ​ watchDurationMillis = 5198 D/LeakCanary( 3071): ​ retainedDurationMillis = 183 D/LeakCanary( 3071): ==================================== D/LeakCanary( 3071): 0 LIBRARY LEAKS D/LeakCanary( 3071): D/LeakCanary( 3071): A Library Leak is a leak caused by a known bug in 3rd party code that you do not have control over. D/LeakCanary( 3071): See https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/#4-categorizing-leaks D/LeakCanary( 3071): ==================================== D/LeakCanary( 3071): METADATA D/LeakCanary( 3071): D/LeakCanary( 3071): Please include this in bug reports and Stack Overflow questions. D/LeakCanary( 3071): D/LeakCanary( 3071): Build.VERSION.SDK_INT: 29 D/LeakCanary( 3071): Build.MANUFACTURER: Sony D/LeakCanary( 3071): LeakCanary version: 2.4 D/LeakCanary( 3071): App process name: com.example.ble_lib_leak D/LeakCanary( 3071): Analysis duration: 4995 ms D/LeakCanary( 3071): Heap dump file path: /data/user/0/com.example.ble_lib_leak/files/leakcanary/2020-09-30_22-35-03_352.hprof D/LeakCanary( 3071): Heap dump timestamp: 1601498111563 D/LeakCanary( 3071): ==================================== W/ActivityThread( 3071): handleWindowVisibility: no activity for token android.os.BinderProxy@fc8ac1e ```
b055man commented 3 years ago

@dariuszseweryn @mikolak I run a similar test using the Flutter Reactive Ble package (https://pub.dev/packages/flutter_reactive_ble), which uses Polidea's rxandroidble library too (it interacts with it directly, not via the MultiplatformBleAdapter) - the difference is that they use the most-up-to-date version of the rxandroidble library, whereas the MultiAdapter still uses a 2-years old version (1.7.1). The RxStateObservable leak was fixed in version 1.10.0 https://github.com/Polidea/RxAndroidBle/pull/575 Isn't this the cause then?

b055man commented 3 years ago

@dariuszseweryn another updated: as expected, using a newer version of the rxandroidble library solves this issue... but it's not that easy to migrate, as it now uses RxJava2. However, thanks to @JamesMcIntosh PR here: https://github.com/Polidea/MultiPlatformBleAdapter/pull/67 I managed to build FlutterBlePlugin using an updated version of MultiPlatformBleAdapter and the latest rxandroidble library. And guess what - the issue does not reproduce there..

So, guys @dariuszseweryn @mikolak - any chance to pursue that James's PR and moving MultiPlatformBleAdapter to RxJava2, to allow using recent versions of the rxandroidble library?

b055man commented 3 years ago

@mikolak @dariuszseweryn Guys, can you comment, please?

mikolak commented 3 years ago

Hi!

Sorry for the long silence, I'm completely swamped with work right now. I'd love to migrate it to new RxAndroidBLE, but I seem to recall there might be some issue with global error listeners (?). I hope to have some time from November. I'll consult it internally and try to get back to you, though I can't promise timely manner right now.

Thank you for investigating it so thoroughly!

b055man commented 3 years ago

@mikolak Thanks for providing the rough timeframe - I will bump the topic again sometime in Nov then, unless there are some updates before that time.

b055man commented 3 years ago

@mikolak @dariuszseweryn Hi guys, any updates on this one?

mikolak commented 3 years ago

@b055man I've just released 3.0.0-beta (https://github.com/Polidea/FlutterBleLib/releases/tag/v3.0.0-beta) with RxAndroidBle 2. Give it a go, lemme know if you encounter any issues. Let me know if everything works fine as well, as this will have to leave beta some day.

Closing for now.

b055man commented 3 years ago

@mikolak Thanks. We'll give it a spin and I'll let you know the results.

PiotrJozefow commented 3 years ago

I'm still experiencing memory leak with version 3.0.0-beta. When peripherial scan is running for about 3-5 minutes, calling peripherial.connect() causes sudden memory spike to up 2GB and app crashes. Screenshot 2021-03-28 at 00 01 48

JamesMcIntosh commented 3 years ago

@PiotrJozefow I had a look last week and found a source of leaking, it's RxJava subscriptions in the MultiPlatformBleAdapter, will look at at PR when I get a moment. See: https://github.com/Polidea/FlutterBleLib/issues/563 and https://github.com/Polidea/MultiPlatformBleAdapter/issues/72

PiotrJozefow commented 3 years ago

@JamesMcIntosh I have no idea why but the memory leak I have occurs only when I have flutter animationController running on repeat at the time of peripherial scan. Thanks for fixing this and I'm really looking forward to your PR being merged 💪

JamesMcIntosh commented 3 years ago

@PiotrJozefow are you repeatedly calling connect() or another method in the library, your animationController may be continuously refreshing the widget... add some breakpoints and see what happening.

mikolak commented 3 years ago

@bo55man @JamesMcIntosh have you encountered any issues using the beta?

JamesMcIntosh commented 3 years ago

@mikolak I haven't had a chance to get back to it yet but I'm pretty sure it was affected by the same problem because of the way RxJava2 is used with subscriptions / observales. https://github.com/Polidea/MultiPlatformBleAdapter/issues/72