StickyEvents in Android Observables? - android

What is the equivalent of getStickyEvent() from EventBus in RxJava.
I would like to subscribe to observables from "screens" that are not in
the foreground/not active, but at anytime very well may pop in.
If events are continuously happening, I want these "screens" to receive them the next time
they are active/in the foreground.
Edit:
It sounds like I should have a replaySubject, and then when the "Screen" comes
to the foreground subcribe to it.....?
BehaviorSubject – emits the last emitted item when subscribed to,
then continues to emit items from the source observable

You already gave the answer yourself but just to confirm: Yes, you would use either BehaviorSubject or ReplaySubject.
After a new subscriber subscribes, they will both emit to that subscriber all items they receive from then onwards. However, each has a little extra beyond that:
BehaviorSubject will always start that sequence by immediately emitting the (one) most recent of the items it has received before the subscriber subscribed, if there was any. If there was none it will emit a default item if one was provided when it was created.
ReplaySubject will always start that sequence by immediately emitting (some or) all of the items it has recevied since its creation in the order that it received it. ReplaySubject can be initialized to limit the number of items it keeps in the cache for later subsribers, or to limit the amount of time that it will keep items in the cache. But (as far as I know), you cannot provide a default value if using a ReplaySubject.
Then, calling
subject.subscribe(new Subscriber<YourEventClass>() {
// implement Subscriber methods here
});
would be more or less equivalent to:
eventbus.registerSticky(this);
and having this implement the callbacks for the EventBus.
Synchronous vs Asynchronous
Note, though, that subscribing like this still makes the delivery of items from the subject asynchronous (like register/registerSticky), as you are in both cases only handing over some callback methods and are not waiting right there for the result to be returned.
I have not used the greenrobot EventBus myself but it seems that getStickyEvent() is synchronous/blocking.
If you want blocking behavior you would have to - instead of subscribing to it - convert the subject to a blocking observable (with subject.toBlocking()).
See here for more on blocking observables:
https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators
but basically you can then transform them to an iterable, or just get the latest item, or a number of other things.

Related

Do Kotlin Flows drop emissions fired in too rapid succession?

I'm writing an Android app that takes screen-touch MotionEvents and converts them into a StateFlow, which is then collected by a Jetpack Compose function that draws them on the screen.
The MotionEvent capture (based around Modifier.motionEventSpy) works as expected, but the resultant Flows frequently don't make it to the Compose function, causing errors, if they're too close together in time.
It happens when two MotionEvents fire almost simulaneously: MotionEvent.ACTION_DOWN and the first MotionEvent.ACTION_MOVE happen in very rapid succession, and perhaps 20 or 30 per cent of the time, the MotionEvent.ACTION_DOWN Flow emission is never collected by the Compose function.
It doesn't happen all the time, or even most of the time, but it happens enough to be a problem.
(Another example of Flow emits not being collected: I've also noticed that, when I have an event listener on exoPlayer, emiting Flows when the exoPlayer state changes, only the second exoPlayer state change ever gets collected if two state changes happen near-simultaneously. For instance, when a media file is played to the very end, exoPlayer's onIsPlayingChanged listener fires at the same time as exoPlayer's onPlaybackStateChanged. If I have both of those listeners emiting on the same StateFlow, only one of them will ever be collected.)
So I'm thinking Kotlin Flows use some sort of mutex lock to prevent two emits/value changes happening at the same time. Is that correct?
And if that's the case, is there anything I do to ensure every Flow.emit or Flow.value = change makes it to the other side??
Are some types of Flows better than others when it comes to concurrency handling?
That is the expected behaviour of state flow. Sadly, the documentation points it out only on the testing section:
https://developer.android.com/kotlin/flow/test#stateflows
Note that this stream of values is conflated, which means that if values are set in a StateFlow rapidly, collectors of that StateFlow are not guaranteed to receive all intermediate values, only the most recent one.
Can you try using flow { } (I inow is cumbersome)? Or maybe can you try with channels (yep you loose the flow advantages)?

Can RxJava be used to send messages only to the most recent subscriber?

I've been given a requirement that when there are multiple observers, only the most recent subscription should receive messages. I'll illustrate with sloppy code examples.
val fooDisposable = mostRecentSubscriberObservable.subscribe(this::foo)
Any time mostRecentSubscriberObservable emits an item, foo() is called.
Now this runs:
val barDisposable = mostRecentSubscriberObservable.subscribe(this::bar)
Now whenever an item is emitted, bar() is called, because barDisposable is the most recent subscriber. foo() is NOT called.
Now this runs:
barDisposable.dispose()
Now whenever an item is emitted, foo() is called. Now that barDisposable is gone, fooDisposable is the most recent subscriber, and receives the item.
Short of creating a custom Subject class (which seems rather complex), is there a way to do this - any existing Subject or combination of operators which would accomplish it? I can't seem to find anything like that.

RxJava Observable subscription must be used

In RxJava 2 subscribe block is giving warning when after subscribing disposable is not used.
Example in Kotlin:
button.clicks().subscribe(...)
It is useful for asynchronous stuff or something that needs to be garbage collected in subscribe blocks, but in many cases it is just plain button click -> navigation stuff.
Question 1
Is it safe to NOT dispose pure view events, because it will get garbage collected with Activity itself or should I explicitly dispose of it, like
button.clicks().subscribe({startActivity(...)})
Question 2
What to do with those warnings? Let them be? Dispose just for the sake of getting rid of warnings?

Handling long running tasks with RxJava

I'm trying to migrate an AsyncTask that sends messages to the server, to use RxJava. Roughly, the task does the following:
1) Creates a message that will be sent (persists to the database)
2) Shows the message to the user (state 'sending')
3) Sends the message to the server (code snippet below)
4) Marks the message as sent or failed (persists to the database)
5) Updates the UI
I've created the required Rx chain which partially looks like this:
public Observable<Message> sendMessage(Message message) {
return mApiClient.sendMessage(message)
.doOnNext(sentMessage -> mDatabase.synchroniseMessage(sentMessage))
.doOnError(e -> {
message.setState(FAILED);
mDatabase.synchroniseMessage(message));
})
.onErrorReturn(e -> Observable.just(message));
When I subscribe to the above, I get a Disposable. Normally I'd add it to the CompositeDisposable object and clear that object then the user has moved to a different view (i.e. fragment). However, in this case, I need to keep running this task to make sure the local database is updated with the task results accordingly.
What would be the most appropriate way to handle this situation? I could simply not add the Disposable into my CompositeDisposable object and therefore it wouldn't be unsubscribed from, but it feels like it could cause issues.
P.S. Showing updates to the user is handled through observing the data in an SQLite table. These events are triggered by the synchroniseMessage method. This is a different subscription which I will simply unsubscribe from, so it's not part of the problem.
One disposes of Disposable as soon as he is no longer interested in it.
In your case you are still interested in the stream regardless user navigates to another screen or no, which means you cannot unsubscribe from it. Which means you cannot add it to CompositeDisposable.
This will result in a situation, when your Activity cannot be garbage collected, because of a implicit reference to it from your Subscription, hence you are creating a memory leak situation.
If you have such a use case, I think you have to perform that request on a component, which will be activity lifecycle independent, like Service.

RxAndroid, event bus and Activity lifecycle

I found a few articles talking about how RxJava/RxAndroid can replace event busses (such as otto)
https://lorentzos.com/rxjava-as-event-bus-the-right-way-10a36bdd49ba#.7a4619qva
https://medium.com/mobiwise-blog/use-rxjava-instead-of-event-bus-libraries-aa78b5023097#.ew28h2urf
A quote from the first article:
Otto from Square got officially deprecated the previous days. In the Android world we can cheer now something like “EventBusses are dead long live RxJava”.
There is one thing I am missing though:
One of the perks of event buses is that they help a lot with the Activity lifecycle in that you don't need to manage registering/unregistering to callbacks manually (and thus avoiding memory leaks easily)
Example flow:
Activity subscribes to an event for getting songs (say SongsAvailableEvent)
We request songs (we make a network request)
We change the device's orientation mid-request
The Activity dies and a new one is built, that is also subscribed to the SongsAvailableEvent
The new activity gets the event and updates the UI, and the old Activity (which is now dead) does not get the event (yay!)
The articles above make it look like this flow is "solved" by RxAndroid/RxJava, but using Rx you still need to subscribe/unsubscribe on an Observable manually when you change the device's orientation. Moreover, if I want to "reuse" the request made in an Observable, I need to somehow persist it so that I will subscribe on that same Observable in the new Activity (I'm not quite sure how to do that, but it is not the point :) ).
My question is: is this problem easily solvable with pure RxAndroid/RxJava, or do I still need to use Rx with an event bus / extend Rx using something like RxLifecycle (which complicates things since I do not manage my Observables in the presentation layer)?
Your Activity's onDestroy can always call unsubscribe.
As for making things work to reuse request- Look into Loaders and LoaderManager. EventBus and RxJava to solve that was never needed.
I would venture to say that there isn't any way out of the fact that at some point in the chain, the Observable has to be tied to the lifecycle of some Android platform object, such as an Activity. Also, because you have not mentioned it as a partial solution, I assume you are avoiding using retained Fragments. If you are creating and holding a reference to the Observable only within your Activity, it is not possible for the results of a request in-flight to survive destruction of the Activity and be automatically subscribed to the new one. In addition, at some point, either during an orientation change, or the Activity finishing in the middle of a network request, your Observable will leak a reference to the Activity (via its subscribe() callback) if it is not unsubscribed on the Activity's onDestroy().
I have found RxLifecycle to be simple to use. My base Activity class has a method on it:
public <T> Observable.Transformer<T,T> bindLifecycleOnMainThread() {
return o -> o.compose(lifecycleProvider.bindToLifecycle())
.observeOn(AndroidSchedulers.mainThread());
}
lifecycleProvider is created as per the instructions for RxLifecycle, depending on how you create your provider. This particular implementation uses bindToLifecycle() rather than specifying an explicit lifecycle event, so its use is contextual. Calling it during onResume will cause it to end on onPause. Calling it during onStart will cause it to end on onStop. Calling it other other times will cause it to end on onDestroy. Since this subscription will be updating the UI, it must only be observed on the UI thread.
This can then then used in the Activity as follows:
yourObservable.compose(bindLifecycleOnMainThread())
.subscribe(event -> handleEvent(event));
Now, where does this observable come from? Well, there's still no magic, and if you want an Observable to have a longer lifespan than the Activity, that means the Observable must be held by a component that lives longer than the Activity. There are many, many ways to do this, but your particular use case maps well to the new ViewModel library included in the Android Architecture framework. If you were to use ViewModels, your ViewModel would have a method that begins the network request, and would have a PublishSubject or PublishRelay that would emit SongsAvailableEvent objects (though I recommend exposing it to your Activity as only an Observable<SongsAvailableEvent>, not a Subject, for good encapsulation!). Your ViewModel would make the network call and forward the results to your Subject.
Finally, your Activity, when created, will immediately get its ViewModel from the ViewModel registry and subscribe to the Observable<SongsAvailableEvent> (which is a Subject/Relay) exposed by the ViewModel, and then bind it to the Activity's lifecycle, as in the example above. The ViewModel will survive any orientation changes of the Activity, and therefore so will the observable. The Observable will then never attempt to deliver an event to a destroyed Activity and the new Activity will immediately begin listening for events.
I believe this strategy promotes good encapsulation, since the Activity does not concern itself with how the network request gets made, and does not concern itself with how the source Observable is created. The only way that the Activity manipulates the Observable is by choosing what happens when it receives an event, and binding the subscription to the lifecycle of the Activity.
This can be endlessly tweaked and refined by composing your Observables but this should get you on the way.

Categories

Resources