RxAndroid, event bus and Activity lifecycle
Asked Answered
V

2

6

I found a few articles talking about how RxJava/RxAndroid can replace event busses (such as otto)

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)?

Virgate answered 13/1, 2017 at 20:55 Comment(0)
F
0

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.

Frugal answered 13/1, 2017 at 21:4 Comment(1)
I do not want to call unsubscribe manually, that is error prone. With otto, I can place unregister(this) in the Activity's base class, and we are done, but using Rx I need to keep track on my Observables. As for Loaders, they are great for data sets, but they are an over complication for some requestsVirgate
S
0

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.

Scraggy answered 1/9, 2017 at 18:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.