Skip to content

Releases: ReactiveX/RxJava

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

0.16.0

07 Jan 20:43
Compare
Choose a tag to compare

This is a significant release with the following changes:

  • Refactor of Subjects and Subscriptions to non-blocking implementations
  • Many bug fixes, new operators and behavior changes to match Rx.Net.
  • Deprecation of some operators due to renaming or eliminating duplicates
  • The rx.concurrency package has been renamed to rx.schedulers. Existing classes still remain in rx.concurrency but are deprecated. Use of rx.concurrency should be migrated to rx.schedulers as these deprecated classes will be removed in a future release.
  • Breaking changes to Scala bindings. See Release Notes for details.
  • New modules: rxjava-string, rxjava-async-util and rxjava-computation-expressions for operators deemed not applicable to the core library.

  • Pull 516 rxjava-string module with StringObservable
  • Pull 533 Operator: ToAsync
  • Pull 535 Fix compilation errors due to referencing the Android support library directly
  • Pull 545 Fixed Zip issue with infinite streams
  • Pull 539 Zipping a finite and an infinite Observable
  • Pull 541 Operator: SkipUntil
  • Pull 537 Add scala adapters for doOnEach operator
  • Pull 560 Add type variances for doOnEach actions
  • Pull 562 Scala Adaptor Improvements
  • Pull 563 Operator: GroupByUntil
  • Pull 561 Revised Approach to Creating Observables in Scala
  • Pull 565 Operator: GroupJoin v2
  • Pull 567 Operator: Timestamp with Scheduler
  • Pull 568 Use lock free strategy for several Subscription implementations
  • Pull 571 Operator: Sample with Observable v2
  • Pull 572 Multiple Subscriptions to ObserveOn
  • Pull 573 Removed Opening and Closing historical artifacts
  • Pull 575 Operator: SequenceEqual reimplementation
  • Pull 587 Operator: LongCount
  • Pull 586 Fix Concat to allow multiple observers
  • Pull 598 New Scala Bindings
  • Pull 596 Fix for buffer not stopping when unsubscribed
  • Pull 576 Operators: Timer and Delay
  • Pull 593 Lock-free subscriptions
  • Pull 599 Refactor rx.concurrency to rx.schedulers
  • Pull 600 BugFix: Replay Subject
  • Pull 594 Operator: Start
  • Pull 604 StringObservable.join
  • Pull 609 Operation: Timer
  • Pull 612 Operation: Replay (overloads)
  • Pull 628 BugFix: MergeDelayError Synchronization
  • Pull 602 BugFix: ObserveOn Subscription leak
  • Pull 631 Make NewThreadScheduler create Daemon threads
  • Pull 651 Subjects Refactor - Non-Blocking, Common Abstraction, Performance
  • Pull 661 Subscriptions Rewrite
  • Pull 520 BugFix: blocking/non-blocking first
  • Pull 621 Scala: SerialSubscription & From
  • Pull 626 BO.Latest, fixed: BO.next, BO.mostRecent, BO.toIterable
  • Pull 633 BugFix: null in toList operator
  • Pull 635 Conditional Operators
  • Pull 638 Operations: DelaySubscription, TakeLast w/ time, TakeLastBuffer
  • Pull 659 Missing fixes from the subject rewrite
  • Pull 688 Fix SafeObserver handling of onComplete errors
  • Pull 690 Fixed Scala bindings
  • Pull 693 Kotlin M6.2
  • Pull 689 Removed ObserverBase
  • Pull 664 Operation: AsObservable
  • Pull 697 Operations: Skip, SkipLast, Take with time
  • Pull 698 Operations: Average, Sum
  • Pull 699 Operation: Repeat
  • Pull 701 Operation: Collect
  • Pull 707 Module: rxjava-async-util
  • Pull 708 BugFix: combineLatest
  • Pull 712 Fix Scheduler Memory Leaks
  • Pull 714 Module: rxjava-computation-expressions
  • Pull 715 Add missing type hint to clojure example
  • Pull 717 Scala: Added ConnectableObservable
  • Pull 723 Deprecate multiple arity ‘from’
  • Pull 724 Revert use of CurrentThreadScheduler for Observable.from
  • Pull 725 Simpler computation/io naming for Schedulers
  • Pull 727 ImmediateScheduler optimization for toObservableIterable

Artifacts: Maven Central