frankelot
frankelot

Reputation: 14409

How to modify subject output type?

I have the following singleton:

public abstract class Store<Input, Output> {
    private BehaviorSubject<Input> subject = BehaviorSubject.create();
    private Observable<Output> observable; //also a singleton

    public final Subscription register(Subscriber<Output> subscriber) {
        if (observable == null) {
            observable = subject.compose(getTransformer()); //is this hot?
        }
        return observable.subscribe(subscriber);
    }

    public final void unregister(Subscription subscription) { //unregister }

    //applies operators to the original stream to transform Input -> Output
    protected abstract Observable.Transformer<Input, Output> getTransformer();

    public final void onNext(Input event) { subject.onNext(event);}
}

Problem: When I rotate the device or minimise the app the observable dies (aborts network execution). Is subject.compose() returning a cold observable, if so, why?.

I tried using publish.autoConnect() / share() to make it hot, now it doesn't die upon rotation... but the BehaviourSubject breaks. When I rotate the device and I don't get the first value upon subscription.

How can I transform the output value of a subject and still have it behave as a subject? SAMPLE PROJECT

LOG:

USER: hits button to fetch from network
D: Retrieving from
network... D: Network request executed successfully
D: Caching to memory
USER: hits button to fetch from network again
D: Retrieving from network...
USER: pressed home button, app backgrounded
D:.unsubscribe()

Upvotes: 2

Views: 260

Answers (1)

frankelot
frankelot

Reputation: 14409

Turns out, I've been using Subjects wrong all along. Here's the correct version of what I wanted to implement above:

 public abstract class RxStore<Input, Output> {
        private BehaviorRelay<Output> relay;

        public final Subscription register(Subscriber<Output> subscriber) {
            if (relay == null) {
                relay = BehaviorRelay.create(defaultValue());
            }
            return relay.subscribe(subscriber);
        }

        public final void unregister(Subscription subscription) {
            if (subscription != null && !subscription.isUnsubscribed()) {
                subscription.unsubscribe();
            }
        }

        public void execute(Input event) {
            buildObservable(event).subscribe(relay);
        }

        /**
         * @return the first or default value emitted to subscribers
         */
        protected Output defaultValue() {
            return null;
        }

        /**
         * @return an buildObservable responsible of handling its own errors.
         */
        protected abstract Observable<Output> buildObservable(Input event);
    }

Edit:

I've found this approach very useful. I'm using this approach in prod and I've written an article about this:

https://medium.com/@FerRaviola/rxandroid-an-event-bus-on-steroids-9699e93eca98#.tqbxleo4h

Upvotes: 0

Related Questions