Skip to content

Releases: ReactiveX/RxJava

0.17.1

13 Mar 22:53
Compare
Choose a tag to compare
  • Pull 953 Make ObserveOnTest.testNonBlockingOuterWhileBlockingOnNext deterministic
  • Pull 930 Initial commit of the Android samples module
  • Pull 938 OperatorWeakBinding (deprecates OperatorObserveFromAndroidComponent)
  • Pull 952 rxjava-scala improvements and reimplemented the amb operator
  • Pull 955 Fixed ReplaySubject leak
  • Pull 956 Fixed byLine test to use line.separator system property instead of \n.
  • Pull 958 OperatorSkipWhile
  • Pull 959 OperationToFuture must throw CancellationException on get() if cancelled
  • Pull 928 Fix deadlock in SubscribeOnBounded
  • Pull 960 Unit test for "Cannot subscribe to a Retry observable once all subscribers unsubscribed"
  • Pull 962 Migrate from SynchronizedObserver to SerializedObserver

Artifacts: Maven Central

0.17.0

10 Mar 19:04
Compare
Choose a tag to compare

Version 0.17.0 contains some significant signature changes that allow us to significantly improve handling of synchronous Observables and simplify Schedulers. Many of the changes have backwards compatible deprecated methods to ease the migration while some are breaking.

The new signatures related to Observable in this release are:

// A new create method takes `OnSubscribe` instead of `OnSubscribeFunc`
public final static <T> Observable<T> create(OnSubscribe<T> f)

// The new OnSubscribe type accepts a Subscriber instead of Observer and does not return a Subscription
public static interface OnSubscribe<T> extends Action1<Subscriber<? super T>>

// Subscriber is an Observer + Subscription
public abstract class Subscriber<T> implements Observer<T>, Subscription

// The main `subscribe` behavior receives a Subscriber instead of Observer
public final Subscription subscribe(Subscriber<? super T> subscriber)

// Subscribing with an Observer however is still appropriate
// and the Observer is automatically converted into a Subscriber
public final Subscription subscribe(Observer<? super T> observer)

// A new 'lift' function allows composing Operator implementations together
public <R> Observable<R> lift(final Operator<? extends R, ? super T> lift)

// The `Operator` used with `lift`
public interface Operator<R, T> extends Func1<Subscriber<? super R>, Subscriber<? super T>>

Also changed is the Scheduler interface which is much simpler:

public abstract class Scheduler {
    public Subscription schedule(Action1<Scheduler.Inner> action);
    public Subscription schedule(Action1<Scheduler.Inner> action, long delayTime, TimeUnit unit);
    public Subscription schedulePeriodically(Action1<Scheduler.Inner> action, long initialDelay, long period, TimeUnit unit);
    public final Subscription scheduleRecursive(final Action1<Recurse> action)
    public long now();
    public int degreeOfParallelism();

    public static class Inner implements Subscription {
        public abstract void schedule(Action1<Scheduler.Inner> action, long delayTime, TimeUnit unit);
        public abstract void schedule(Action1<Scheduler.Inner> action);
        public long now();
    }

    public static final class Recurse {
        public final void schedule();
        public final void schedule(long delay, TimeUnit unit);
    }
}

This release applies many lessons learned over the past year and seeks to streamline the API before we hit 1.0.

As shown in the code above the changes fall into 2 major sections:

1) Lift/Operator/OnSubscribe/Subscriber

Changes that allow unsubscribing from synchronous Observables without needing to add concurrency.

2) Schedulers

Simplification of the Scheduler interface and make clearer the concept of "outer" and "inner" Schedulers for recursion.

Lift/Operator/OnSubscribe/Subscriber

New types Subscriber and OnSubscribe along with the new lift function have been added. The reasons and benefits are as follows:

1) Synchronous Unsubscribe

RxJava versions up until 0.16.x are unable to unsubscribe from a synchronous Observable such as this:

Observable<Integer> oi = Observable.create(new OnSubscribe<Integer>() {

    @Override
    public void call(Observer<? super Integer> Observer) {
        for (int i = 1; i < 1000000; i++) {
            subscriber.onNext(i);
        }
        subscriber.onCompleted();
    }
});

Subscribing to this Observable will always emit all 1,000,000 values even if unsubscribed such as via oi.take(10).

Version 0.17.0 fixes this issue by injecting the Subscription into the OnSubscribe function to allow code like this:

Observable<Integer> oi = Observable.create(new OnSubscribe<Integer>() {

    @Override
    public void call(Subscriber<? super Integer> subscriber) {
        // we now receive a Subscriber instead of Observer
        for (int i = 1; i < 1000000; i++) {
            // the OnSubscribe can now check for isUnsubscribed
            if (subscriber.isUnsubscribed()) {
                return;
            }
            subscriber.onNext(i);
        }
        subscriber.onCompleted();
    }

});

Subscribing to this will now correctly only emit 10 onNext and unsubscribe:

// subscribe with an Observer
oi.take(10).subscribe(new Observer<Integer>() {

    @Override
    public void onCompleted() {

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onNext(Integer t) {
        println("Received: " + t);
    }

})

Or the new Subscriber type can be used and the Subscriber itself can unsubscribe:

// or subscribe with a Subscriber which supports unsubscribe
oi.subscribe(new Subscriber<Integer>() {

    @Override
    public void onCompleted() {

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onNext(Integer t) {
        println("Received: " + t);
        if(t >= 10) {
            // a Subscriber can unsubscribe
            this.unsubscribe();
        }
    }

})
2) Custom Operator Chaining

Because Java doesn't support extension methods, the only approach to applying custom operators without getting them added to rx.Observable is using static methods. This has meant code like this:

MyCustomerOperators.operate(observable.map(...).filter(...).take(5)).map(...).subscribe()

In reality we want:

observable.map(...).filter(...).take(5).myCustomOperator().map(...).subscribe()

Using the newly added lift we can get quite close to this:

observable.map(...).filter(...).take(5).lift(MyCustomOperator.operate()).map(...).subscribe()

Here is how the proposed lift method looks if all operators were applied with it:

Observable<String> os = OBSERVABLE_OF_INTEGERS.lift(TAKE_5).lift(MAP_INTEGER_TO_STRING);

Along with the lift function comes a new Operator signature:

public interface Operator<R, T> extends Func1<Subscriber<? super R>, Subscriber<? super T>>

All operator implementations in the rx.operators package will over time be migrated to this new signature.

NOTE: Operators that have not yet been migrated do not work with synchronous unsubscribe.

3) Simpler Operator Implementations

The lift operator injects the necessary Observer and Subscription instances (via the new Subscriber type) and eliminates (for most use cases) the need for manual subscription management. Because the Subscription is available in-scope there are no awkward coding patterns needed for creating a Subscription, closing over it and returning and taking into account synchronous vs asynchronous.

For example, the body of fromIterable is simply:

public void call(Subscriber<? super T> o) {
    for (T i : is) {
        if (o.isUnsubscribed()) {
            return;
        }
        o.onNext(i);
    }
    o.onCompleted();
}

The take operator is:

public Subscriber<? super T> call(final Subscriber<? super T> child) {
        final CompositeSubscription parent = new CompositeSubscription();
        if (limit == 0) {
            child.onCompleted();
            parent.unsubscribe();
        }

        child.add(parent);
        return new Subscriber<T>(parent) {

            int count = 0;
            boolean completed = false;

            @Override
            public void onCompleted() {
                if (!completed) {
                    child.onCompleted();
                }
            }

            @Override
            public void onError(Throwable e) {
                if (!completed) {
                    child.onError(e);
                }
            }

            @Override
            public void onNext(T i) {
                if (!isUnsubscribed()) {
                    child.onNext(i);
                    if (++count >= limit) {
                        completed = true;
                        child.onCompleted();
                        unsubscribe();
                    }
                }
            }

        };
    }
4) Recursion/Loop Performance with Unsubscribe

The fromIterable use case is 20x faster when implemented as a loop instead of recursive scheduler (see a18b8c1).

Several places we can remove recursive scheduling used originally for unsubscribe support and use a loop instead.

Schedulers

Schedulers were greatly simplified to a design based around Action1<Inner>.

public abstract class Scheduler {
    public Subscription schedule(Action1<Scheduler.Inner> action);
    public Subscription schedule(Action1<Scheduler.Inner> action, long delayTime, TimeUnit unit);
    public Subscription schedulePeriodically(Action1<Scheduler.Inner> action, long initialDelay, long period, TimeUnit unit);
    public final Subscription scheduleRecursive(final Action1<Recurse> action)
    public long now();
    public int degreeOfParallelism();

    public static class Inner implements Subscription {
        public abstract void schedule(Action1<Scheduler.Inner> action, long delayTime, TimeUnit unit);
        public abstract void schedule(Action1<Scheduler.Inner> action);
        public long now();
    }

    public static final class Recurse {
        public final void schedule();
        public final void schedule(long delay, TimeUnit unit);
    }
}

This design change originated from three findings:

  1. It was very easy to cause memory leaks or inadvertent parallel execution since the distinction between outer and inner scheduling was not obvious.

To solve this the new design explicitly has the outer Scheduler and then Scheduler.Inner for recursion.

  1. The passing of state is not useful since scheduling over network boundaries with this model does not work.

In this new design all state passing signatures have been removed. This was determined while implem...

Read more

0.17.0 Release Candidate 7

10 Mar 19:05
Compare
Choose a tag to compare
Pre-release

Pre-release of 0.17.0 for testing. See details at #802.

0.17.0 Release Candidate 6

25 Feb 23:31
Compare
Choose a tag to compare
Pre-release

Pre-release of 0.17.0 for testing. See details at #802.

Compared with RC5 this has changes to:

  • Clojure bindings
  • TestSubscriber

0.17.0 Release Candidate 5

25 Feb 08:06
Compare
Choose a tag to compare
Pre-release

Pre-release of 0.17.0 for testing. See details at #802.

Compared with RC4 this has changes to:

  • error handling of operators via lift
  • debug module

0.17.0 Release Candidate 4

21 Feb 23:02
Compare
Choose a tag to compare
Pre-release

Pre-release of 0.17.0 for testing. See details at #802.

Compared with RC3 this has changes to:

  • doOnTerminate
  • zip bug fix
  • execution hook cleanup

0.17.0 Release Candidate 3

21 Feb 22:08
Compare
Choose a tag to compare
Pre-release

Pre-release of 0.17.0 for testing. See details at #802.

Compared with RC2 this has changes to:

  • a new RxJavaDefaultSchedulers plugin
  • bug fixes

0.17.0 Release Candidate 2

18 Feb 16:31
Compare
Choose a tag to compare
Pre-release

Pre-release of 0.17.0 for testing. See details at #802.

Compared with RC1 this has changes to:

  • observeOn
  • subscribeOn
  • new unsubscribeOn operator
  • Android and Swing component Subscriptions
  • movement of rx.util packages
  • error handling

0.17.0 Release Candidate 1

14 Feb 21:55
Compare
Choose a tag to compare
Pre-release

Pre-release of 0.17.0 for testing. See details at #802.

In particular please test:

  • observeOn
  • subscribeOn
  • groupBy
  • zip
  • Scheduler

0.16.1

15 Jan 07:34
Compare
Choose a tag to compare
  • Pull 730 Improve Error Handling and Stacktraces When Unsubscribe Fails
  • Pull 720 Added Observable.timeout wrappers to scala adapter
  • Pull 731 Fix non-deterministic unit test
  • Pull 742 Build with Gradle 1.10
  • Pull 718 Merge overloads
  • Pull 733 Buffer with Observable boundary
  • Pull 734 Delay with subscription and item delaying observables
  • Pull 735 Window with Observable boundary
  • Pull 736 MergeMap with Iterable and resultSelector overloads
  • Pull 738 Publish and PublishLast overloads
  • Pull 739 Debounce with selector
  • Pull 740 Timeout with selector overloads
  • Pull 745 Fixed switch bug
  • Pull 741 Zip with iterable, removed old aggregator version and updated tests
  • Pull 749 Separated Android test code from source
  • Pull 732 Ported groupByUntil function to scala-adapter

Artifacts: Maven Central