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?
Related
I'm asking this from an Android perspective, but this should apply to RxJava in general.
As a best practice, should my view always dispose of even short lived Completable, Single, Maybe and terminating Observable Rx types that should terminate in short order, but could be still be executing when the user closes the view? I'm aware that when the Rx chain terminates, it is disposed but this could potentially occur sometime after the view is closed.
For example, a Single that's performing an HTTP GET. The call will complete, but it may be after the view destroyed, temporarily preventing garbage collection.
And if a CompositeDisposable is used to collect such Disposables in a long-lived view, I would think that care should be taken to clear() or otherwise remove these Disposables regularly to prevent unbounded growth in the size of CompositeDisposable?
As a best practice, should my view always dispose of even short lived Completable, Single, Maybe and terminating Observable Rx types
If you're sharing the code with others, as a best practice, I am going to say yes you should dispose. While it may seem like extra boiler plate, it will prevent the next developer from trying to hook into the your subscription code and attempt to access components that may no longer exist (ie a Context after a Fragment is detached... etc).
And if a CompositeDisposable is used to collect such Disposables in a long-lived view, I would think that care should be taken to clear() or otherwise remove these Disposables regularly to prevent unbounded growth in the size of CompositeDisposable?
I do want to point out that in rxjava2, CompositeDisposables have a state; once you call dispose(), any subsequent Disposable added will be immediately disposed of. So as a best practice:
create a fresh CompositeDisposable at the beginning of the lifecycle (onCreate(...) etc)
take care of dispose() in onDestroy(...) because by that point your callback practically has no value and is just holding onto resources.
It's a good practice to dispose CompositeDisposable in onPause or onDestroy to avoid such problems.
// Using clear will clear all, but can accept new disposable
disposables.clear();
// Using dispose will clear all and set isDisposed = true, so it will not accept any new disposable
disposables.dispose();
call clear in onResume
use dispose in onDestroy
I have similar issue as well, sometime task can be disposed but sometime are not, which cause app crash randomly. However, everything get work after I update rxJava/Rxandroid version.
Original Rx version:
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.1.5'
And then upgrade to
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.6'
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.
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.
Can someone confirm if calling unSubscribe on the Observable is the right way to cancel a request?
Annoyingly, the Nexus 7 issues duplicate network requests and the first call needs to be thrown away as the calling Activity has been destroyed.
I got round this by checking the callback (my own) is not null before trying to use it.
Unsubscribing however, seems a better solution but could not find any information if this is the correct way to go.
I noticed that Retrofit would throw an internal error (InterruptedException) but that did not bubble up to my error callback - a good thing!
To my knowledge, yes unsubscribing is a better way. If you haven't got RxJava already installed as a dependency I recommend you do so, it makes managing Observables much easier. RxJava is also totally compatible with Retrofit.
Then you can unsubscribe from a Subscription in the onDestroy method as so:
#Override
public void onDestroy() {
if (bookingSubscription != null) {
bookingSubscription.unsubscribe();
}
super.onDestroy();
}
Use of the onDestroy method will be useful when applied to your scenario too. You can unsubscribe from Observables in there.
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.