All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.nextop.rx.RxLifecycleBinder Maven / Gradle / Ivy

package io.nextop.rx;

import immutablecollections.ImSet;
import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.Subscription;
import rx.functions.Action0;
import rx.observers.Subscribers;
import rx.subscriptions.BooleanSubscription;
import rx.subscriptions.CompositeSubscription;

import javax.annotation.Nullable;

public interface RxLifecycleBinder extends Subscription {

    void reset();

    /** Wraps the given observable in an observable bound to the lifecyle of a container.
     * The wrapper allocates on subscribe and cleans up on unsubscribe.
     * Unsubscription of all can be forced with {@link #reset}.
     *
     * This ensures:
     * - the subscription is connected to the source when started
     * - the subscription is dropped from the source when stopped
     *   (onComplete is not called, because the subscription will resume when restarted) */
     Observable bind(Observable source);
    void bind(Subscription sub);




    /** Binds to an internal lifecycle start/stop. */
    final class Lifted implements RxLifecycleBinder {

        private ImSet> binds = ImSet.empty();
        private final CompositeSubscription subscriptions = new CompositeSubscription();

        private boolean connected = false;
        private boolean closed = false;

        @Nullable
        private Subscription cascadeSubscription = null;


        public Lifted() {
        }


        public void start() {
            if (closed) {
                throw new IllegalStateException();
            }
            if (!connected) {
                connected = true;
                for (Bind bind : binds) {
                    bind.connect();
                }
            }
        }
        public void stop() {
            if (closed) {
                throw new IllegalStateException();
            }
            if (connected) {
                connected = false;
                for (Bind bind : binds) {
                    bind.disconnect();
                }
            }
        }

        public void close() {
            if (closed) {
                throw new IllegalStateException();
            }
            closed = true;
            removeCascadeUnsubscribe();
            clear();
            subscriptions.unsubscribe();
        }


        public void removeCascadeUnsubscribe() {
            if (null != cascadeSubscription) {
                cascadeSubscription.unsubscribe();
                cascadeSubscription = null;
            }
        }

        // replaces previous cascade parent
        public void cascadeUnsubscribe(@Nullable RxLifecycleBinder parent) {
            removeCascadeUnsubscribe();
            if (null != parent) {
                cascadeSubscription = parent.bind(MoreObservables.hanging()).doOnCompleted(new Action0() {
                    @Override
                    public void call() {
                        unsubscribe();
                    }
                }).subscribe();
            }
        }


        private void clear() {
            ImSet> _binds = binds;
            binds = ImSet.empty();
            for (Bind bind : _binds) {
                bind.close();
            }
            subscriptions.clear();
        }



        /////// Subscription ///////

        @Override
        public void unsubscribe() {
            close();
        }

        @Override
        public boolean isUnsubscribed() {
            return closed;
        }

        /////// RxLifecycleBinder ///////


        @Override
        public void reset() {
            if (closed) {
                throw new IllegalStateException();
            }
            clear();
        }

        @Override
        public  Observable bind(Observable source) {
            if (closed) {
                return Observable.empty();
            }
            Bind bind = new Bind(source);
            binds = binds.adding(bind);
            if (connected) {
                bind.connect();
            }
            return bind.adapter;
        }

        @Override
        public void bind(Subscription sub) {
            if (closed) {
                sub.unsubscribe();
            } else {
                subscriptions.add(sub);
            }
        }

        private static final class Bind {
            private final Observable source;
            final Observable adapter;

            private boolean closed = false;
            private boolean connected;

            private ImSet subscribers = ImSet.empty();


            Bind(Observable source) {
                this.source = source;

                adapter = Observable.create(new Observable.OnSubscribe() {
                    @Override
                    public void call(Subscriber subscriber) {
                        add(subscriber);
                    }
                });
            }

            final class Bridge {
                final Subscriber subscriber;
                private @Nullable Subscription subscription = null;


                Bridge(Subscriber subscriber) {
                    this.subscriber = subscriber;
                }


                void subscribe(Subscription subscription) {
                    unsubscribe();
                    if (subscriber.isUnsubscribed()) {
                        subscription.unsubscribe();
                    } else {
                        this.subscription = subscription;
                    }
                }

                void unsubscribe() {
                    if (null != subscription) {
                        subscription.unsubscribe();
                        subscription = null;
                    }
                }

                void close() {
                    unsubscribe();
                    if (!subscriber.isUnsubscribed()) {
                        subscriber.onCompleted();
                        subscriber.unsubscribe();
                    }
                }


                public Subscriber inSubscriber() {
                    return Subscribers.from(new Observer() {
                        @Override
                        public void onNext(T t) {
                            if (!subscriber.isUnsubscribed()) {
                                subscriber.onNext(t);
                            }
                        }

                        @Override
                        public void onCompleted() {
                            if (!subscriber.isUnsubscribed()) {
                                subscriber.onCompleted();
                            }
                        }

                        @Override
                        public void onError(Throwable e) {
                            if (!subscriber.isUnsubscribed()) {
                                subscriber.onError(e);
                            }
                        }
                    });
                }

                public Subscription outSubscription() {
                    return BooleanSubscription.create(new Action0() {
                        @Override
                        public void call() {
                            unsubscribe();
                            subscribers = subscribers.removing(Bridge.this);
                        }
                    });
                }





            };



            private void add(final Subscriber subscriber) {
                Bridge bridge = new Bridge(subscriber);
                subscribers = subscribers.adding(bridge);

                subscriber.add(bridge.outSubscription());
                connect(bridge);
            }

            void connect() {
                if (closed) {
                    throw new IllegalStateException();
                }
                if (!connected) {
                    connected = true;
                    for (Bridge bridge : subscribers) {
                        connect(bridge);
                    }
                }
            }
            void connect(Bridge bridge) {
                if (connected && !bridge.subscriber.isUnsubscribed()) {
                    bridge.subscribe(source.subscribe(bridge.inSubscriber()));
                }
            }


            void disconnect() {
                if (closed) {
                    throw new IllegalStateException();
                }
                if (connected) {
                    connected = false;
                    for (Bridge bridge : subscribers) {
                        bridge.unsubscribe();
                    }
                }
            }

            // implies disconnect
            void close() {
                if (closed) {
                    throw new IllegalStateException();
                }
                disconnect();
                if (!closed) {
                    closed = true;
                    for (Bridge bridge : subscribers) {
                        bridge.close();
                    }
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy