2
votes

The problem I want to solve

I am building a kind of MVP (or whatever you call it) where views can subscribe and unsubscribe to the same model.

At start, the view subscribes to the model. The model is empty and the view displays nothing. When calling a method (for instance, when a user clicks), a request is made over the network. The result will populate the model, which will notify the view of the change.

If another view subscribes, it should be notified with the model current value, without any call over the network. If all views unsubscribe while the request is ongoing, the model shall still be popuplated, so that another view can

My attempt

I am using Retrofit with RxJava2 to make the requests over the network, which provides a cold Observable.

For the model, I want to build a hot Observable that returns the last emitted item to any new subscriber. How can I make the hot observable get its emitted items from the cold Observable?

So far, I could achieve this with a BehaviourSubject:

// The same model is provided to all views through a getter
BehaviorSubject<User> model = BehaviorSubject.create();

// Make request
Observable<User> resource = getResourceOverNetwork(3);
resource.subscribe(new Consumer<User>() {
    @Override
    public void accept(@NonNull User user) throws Exception {
        model.onNext(user);
    }
});

This solution feels wrong as I have to manually forward events from the cold Observable to the BehaviourSubject.

1
If you only want to make your result from retrofit to be a hot observable. You can use replay(1).autoConnect - Phoenix Wang
With replay(1).autoConnect(), how should I make a new request over the network, with new parameters, so that the model gets updated? - Multicolaure
@LaureC. there are 2 approaches: relay using subject so you post your action to subject and model susbscribes to this subject and makes a request. Or create observable using emitter. However, emitter might not suite MVP pattern, but works well for RxPM. - MightySeal
Many solutions actually use subject, you can do simply resource.subscribe(model) - Lamorak
@Lamorak, I had forgotten that Subject is also an Observer, thank you. However it doesn't work in my case as resource completes after emiting the first value, making the model complete as well. Making the request a second time will not make the model emit an item as it is complete. - Multicolaure

1 Answers

0
votes

Thanks to your comments, I could successfully implement what I needed with RxRelay.

Create the model:

// The same model is provided to all views through a getter
BehaviorRelay<User> model = BehaviorRelay.create();

model.subscribe(view);

Make the requests at any time:

// Make request
Observable<User> resource = getResourceOverNetwork(3);
resource.subscribe(model);

// Make a new request
getResourceOverNetwork(4).subscribe(model);

It is much easier now and much cleaner. Thanks a lot!