Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RxJava2 #67

Merged
merged 1 commit into from
Dec 11, 2020
Merged

RxJava2 #67

merged 1 commit into from
Dec 11, 2020

Conversation

JamesMcIntosh
Copy link
Contributor

Upgrades the RxAndroidBLE library to the latest version and basic RxJava2 changes to support the new version.

I haven't looked at changing the architecture of the project when implementing the RxJava2 changes or updating the java coding style.

@CLAassistant
Copy link

CLAassistant commented Sep 15, 2020

CLA assistant check
All committers have signed the CLA.

@b055man
Copy link

b055man commented Oct 3, 2020

@dariuszseweryn @mikolak Any reasons not to pull this in? This will resolve the leak issue that is present in the current version of the Adapter (due to the old version of rxandroidble library that is used).
dotintent/FlutterBleLib#528

@mikolak
Copy link
Collaborator

mikolak commented Nov 10, 2020

Hi, @JamesMcIntosh! Which library have you checked it against, FlutterBleLib or react-native-ble-plx? Have you encountered any performance issues?
IIRC there could be some issues with backpressure handling if there was another library using RxJava2. @dariuszseweryn do I remember correctly?

@JamesMcIntosh
Copy link
Contributor Author

Hi @mikolak , I've tested it against FlutterBleLib. I haven't encountered any performance issues, it's been pretty smooth.
Isn't the scheduling and backpressure strategy encapsulated in the RxAndroidBle project already?

I would expect the only time it should be of any real significance is if you are monitoring a characteristic which is pushing a lot of data and the device can't keep up and buffers till out of memory. I guess you could pass in an explicit BackpressureStrategy enum to specify the strategy which it employs.

@mikolak
Copy link
Collaborator

mikolak commented Nov 10, 2020

I didn't remember correctly - there are changes to how errors work. If two functions in a chain throw error, then only the first error is delivered to the error handler and the other one goes to a globally registered error handler and there can be only one such registered, so if there were other native dependencies setting that there would be a race condition on which one is used.

Since we don't want to use the global listener, there supposedly is a risk of a crash killing the application. The possible culprit is setting notifications. I'm not sure how - possibly when both the notification stream and writing the descriptor result in an error.

I'll try to get back to you once I have something more solid. Can you check that scenario? I might release them as a dev version.

@JamesMcIntosh
Copy link
Contributor Author

Hi @mikolak,
Here is the documentation in regards to error handling changes in RxJava2.
https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling

The documentation suggest that intermediate libraries should not be setting a global error handler, instead they suggest that the final application is the one to set this handler.

To stick with this recommendation then I would recommend updating the documentation in FlutterBleLib and react-native-ble-plx to add their setup instructions details of the setting a global error handler.

I would also recommend a major version bump to these libraries even though it is dependency upgrade should normally be a minor version. Additionally there could be a check added to the libraries which looks to see if the global error handler has actually be registered and log a warning message / exception.

@dariuszseweryn
Copy link
Collaborator

Hello @JamesMcIntosh

Thanks for your input. The problem with using RxJava2 with a global error handler is that this may interfere with other libraries that user RxJava2 under the hood making it a bad experience and potentially long hours spent on looking for hard to trace crashes.

I have a plan, for quite a while now, to create a RxJava2 based API that would behave differently — it would complete all streams originated from RxBleConnection. This way we should be able to have an API that would be UndeliverableException free.

I dream of a set of libs that have stable, up to date dependencies and are as easy to use as possible. I want to get to that but having experience with RxAndroidBle I did not decided to bump its version in this project because of potential fallout.

@JamesMcIntosh
Copy link
Contributor Author

Hi @dariuszseweryn,

Thanks for the background information, it sounds like a solid refactor to rebuild the API, encapsulating the RxJava2 implementation should shield the user from any problems.

I feel like I'm not seeing the problem with RxJava2 the same way as you as I'm not sure how it may interfere with other libraries, is there something specific you're thinking of?

The main issue I had was the RxAndroidBle - RxJava1 dependency in this library is that it doesn't work with the bluetooth device I'm connecting to and has fallen behind with fixes. I've used RxAndroidBle - RxJava2 for 2 months now and it's worked perfectly, in my testing UndeliverableException were caught in the global error handler caught and allowed the app to continue as expected.

I also see that RxJava2 will stop development Feb 2021, is there a plan to migrate up to RxJava3 also?
https://github.com/ReactiveX/RxJava/wiki/What's-different-in-3.0

@b055man
Copy link

b055man commented Nov 20, 2020

@mikolak @dariuszseweryn guys, so what's your take on this? Any chances on pulling in that change soon? Or have you got some other proposal maybe (with a timeframe, if possible)? Those leaking receivers is a real issue that would be nice to address you know :)

@mikolak
Copy link
Collaborator

mikolak commented Dec 11, 2020

I'll be releasing this as 1.0.0-beta. Until it's battle-tested I'll be keeping it so.

@mikolak mikolak merged commit 4862494 into dotintent:master Dec 11, 2020
@b055man
Copy link

b055man commented Dec 11, 2020

@mikolak Will there be a beta version of the Flutter BLE plugin too that includes this beta version of the adapter? Or are you planning to verify it using other plugins?

@mikolak
Copy link
Collaborator

mikolak commented Dec 11, 2020

Yeah, preparing betas of MBA and FBL. I was planning to make you the tester. 😉

@b055man
Copy link

b055man commented Dec 11, 2020

Sounds good! @mikolak by the way, is BleEmulator affected by this change (I guess it is?) If so, would it be possible to post a beta for it too (we're using it as well)?

@b055man
Copy link

b055man commented Dec 11, 2020

@mikolak you should consider bumping version number of the RxAndroidBle lib once again now that @dariuszseweryn have merged another leak-related fix.. dariuszseweryn/RxAndroidBle#708 😀

@mikolak
Copy link
Collaborator

mikolak commented Dec 11, 2020

Sounds good! @mikolak by the way, is BleEmulator affected by this change (I guess it is?) If so, would it be possible to post a beta for it too (we're using it as well)?

It's not, BLEmulator only uses the BleAdapter interface. Should work just as well with this new version, unless pub complains about versions. Let me know if that happens.

@mikolak you should consider bumping version number of the RxAndroidBle lib once again now that @dariuszseweryn have merged another leak-related fix.. dariuszseweryn/RxAndroidBle#708 😀

One step at a time. 😉 If all works well, I'll get to updating it, though not sure if that will happen in December.

@b055man
Copy link

b055man commented Dec 16, 2020

The main issue I had was the RxAndroidBle - RxJava1 dependency in this library is that it doesn't work with the bluetooth device I'm connecting to and has fallen behind with fixes. I've used RxAndroidBle - RxJava2 for 2 months now and it's worked perfectly, in my testing UndeliverableException were caught in the global error handler caught and allowed the app to continue as expected.

Hi @JamesMcIntosh,
I'm playing with the lib and generally it works nice but I've just noticed an UndeliverableException when turning off BT adapter while connected to the device.
Do you basically catch that exception in your app and ignore it or is there another way of specifying the error handler for the plugin? By the way, I'm using FlutterBleLib (which uses MutiplatformBleAdapter internally).

Thanks,
A.

@JamesMcIntosh
Copy link
Contributor Author

Hi @b055man,

Yes, I set call the RxJavaPlugins.setErrorHandler in my Android FlutterActivity class as shown in the docs.
https://github.com/Polidea/FlutterBleLib/tree/beta/3.0.0#android

The UndeliverableException is happening because a second exception is being sent to aSubscriber or Consumer which only expects a single error. Since the error has already been thrown then you will have responded in your Flutter onError already.

There is a good explanation and example in the RxJava documentation.
https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling

@b055man would you be able to share what type of Exception is being wrapped by the UndeliverableException?

@mikolak @dariuszseweryn There is some discussion in the RxJava documentation above about catching InterruptionException inside libraries, this may decrease the number of UndeliverableException, maybe we could look at this.

Kind regards
James

@b055man
Copy link

b055man commented Dec 16, 2020

@JamesMcIntosh ah, thank you. I've missed the documentation update apparently.. :)

It's a BleDisconnectedException:

12-16 19:35:21.189 32537 32537 I RxBle#LoadedApk: Adapter state changed: STATE_TURNING_OFF
12-16 19:35:21.198 32537 32537 I RxBle#LoadedApk: Adapter state changed: STATE_TURNING_OFF
12-16 19:35:21.199 32537 32537 V RxBle#DisconnectionRouter: An exception received, indicating that the adapter has became unusable.
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: Connection operations queue to be terminated (MAC='XX:XX:XX:XX:XX:XX')
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: com.polidea.rxandroidble2.exceptions.BleDisconnectedException: Disconnected from MAC='XX:XX:XX:XX:XX:XX' with status -1 (UNKNOWN)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at com.polidea.rxandroidble2.internal.connection.DisconnectionRouter$3.apply(DisconnectionRouter.java:53)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at com.polidea.rxandroidble2.internal.connection.DisconnectionRouter$3.apply(DisconnectionRouter.java:50)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at io.reactivex.internal.operators.observable.ObservableMap$MapObserver.onNext(ObservableMap.java:57)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at io.reactivex.internal.operators.observable.ObservableFilter$FilterObserver.onNext(ObservableFilter.java:52)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at io.reactivex.internal.operators.observable.ObservableConcatMap$ConcatMapDelayErrorObserver$DelayErrorInnerObserver.onNext(ObservableConcatMap.java:506)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at io.reactivex.internal.operators.observable.ObservableMap$MapObserver.onNext(ObservableMap.java:62)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at io.reactivex.internal.operators.observable.ObservableRefCount$RefCountObserver.onNext(ObservableRefCount.java:227)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at io.reactivex.internal.operators.observable.ObservablePublishAlt$PublishConnection.onNext(ObservablePublishAlt.java:177)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at io.reactivex.internal.operators.observable.ObservableUnsubscribeOn$UnsubscribeObserver.onNext(ObservableUnsubscribeOn.java:60)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeOnObserver.onNext(ObservableSubscribeOn.java:58)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at io.reactivex.internal.operators.observable.ObservableCreate$CreateEmitter.onNext(ObservableCreate.java:66)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at com.polidea.rxandroidble2.RxBleAdapterStateObservable$1$1.onReceive(RxBleAdapterStateObservable.java:67)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1556)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at android.app.-$$Lambda$LoadedApk$ReceiverDispatcher$Args$_BumDX2UKsnxLVrE6UJsJZkotuA.run(Unknown Source:2)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at android.os.Handler.handleCallback(Handler.java:883)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at android.os.Handler.dispatchMessage(Handler.java:100)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at android.os.Looper.loop(Looper.java:359)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at android.app.ActivityThread.main(ActivityThread.java:7418)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at java.lang.reflect.Method.invoke(Native Method)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: Caused by: com.polidea.rxandroidble2.exceptions.BleAdapterDisabledException
12-16 19:35:21.235 32537 32537 D RxBle#ConnectionOperationQueue: 	... 21 more
12-16 19:35:21.236 32537   992 V RxBle#Executors: Terminated (MAC='XX:XX:XX:XX:XX:XX')
12-16 19:35:21.243 32537 32537 D RxBle#ClientOperationQueue: QUEUED   DisconnectOperation(51271200)
12-16 19:35:21.243 32537 32628 D RxBle#ClientOperationQueue: STARTED  DisconnectOperation(51271200)
12-16 19:35:21.243 32537 32628 I RxBle#ClientOperationQueue: RUNNING  DisconnectOperation{MAC='XX:XX:XX:XX:XX:XX'}
12-16 19:35:21.252 32537  1003 W System.err: io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | com.polidea.rxandroidble2.exceptions.BleDisconnectedException: Disconnected from MAC='XX:XX:XX:XX:XX:XX' with status -1 (UNKNOWN)
....
....
12-16 19:35:21.257 32537  1003 W System.err: Caused by: com.polidea.rxandroidble2.exceptions.BleAdapterDisabledException
12-16 19:35:21.258 32537  1003 W System.err: 	... 21 more

I wonder a bit why that STATE_TURNING_OFF is printed twice.. if this is something in the lib or could it be caused by multiple subscriptions from the app maybe.. (which shouldn't be the case, but who knows) ? 🤔

@JamesMcIntosh
Copy link
Contributor Author

@b055man Are you able to determine what the other Exception was?
An easy way is to attach the Android debugger from Android Studio and catch all java Exceptions (caught / uncaught) and watch for both of them.

As for seeing two state changes, I had a look at the Plugin code and you may end up with 2 messages if you call createClient without having destroyed it between.I think this is a bug in FlutterBleLibPlugin which will just replace it's BleAdapter each time createClient is called even if it already exists. I've created an issue for this dotintent/FlutterBleLib#563

@b055man
Copy link

b055man commented Dec 16, 2020

@JamesMcIntosh I think the other one was BleAdapterDisabledException that is at the end of my snippet. I will check the app later too.

I'm calling the 'createClient' method only once, upon the app startup and clean up the client upon the app exit.. Not sure how that could end up being called twice, but thanks for catching that, this sounds like a thing to check definitely!

@JamesMcIntosh
Copy link
Contributor Author

@b055man Have a look in the Android debug logs for on native side observed method: to see what is happening, it's logged whenever a plugin function is called.
Log.d(TAG, "on native side observed method: " + call.method);
see FlutterBleLibPlugin.java#L94

@mikolak
Copy link
Collaborator

mikolak commented Dec 16, 2020

Hot restart, perhaps? It just drops any Dart state, so it could potentially create second client without destroying the previous one.

@JamesMcIntosh
Copy link
Contributor Author

@mikolak Does it make sense to guard against the creation on multiple clients then in FlutterBleLibPlugin.setupAdapter(), I created a ticket in dotintent/FlutterBleLib#563?

stephenongunsw added a commit to unsw-gsbme/MultiPlatformBleAdapter that referenced this pull request Jul 20, 2022
…2_test

* commit '7b4c11dd2d94bfd492dbc82bad6e0fde0112e3ae':
  Release 1.0.0-beta
  RxJava2 (dotintent#67)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants