Releases: ReactiveX/RxJava
0.18.1
- Pull 1065 Optimize OperatorSkipLastTimed
- Pull 1073 OperatorBuffer
- Pull 1074 OperatorConcat
- Pull 1088 OperatorToObservableFuture
- Pull 1087 OperatorMergeMap
- Pull 1086 OperatorFinallyDo
- Pull 1085 OperatorDistinctUntilChanged
- Pull 1084 OperatorDistinct
- Pull 1083 OperatorDematerialize
- Pull 1081 OperatorDefer
- Pull 1080 OperatorDefaultIfEmpty
- Pull 1079 OperatorCombineLatest
- Pull 1074 OperatorConcat
- Pull 1073 OperatorBuffer
- Pull 1091 Handle Thrown Errors with UnsafeSubscribe
- Pull 1092 Restore ObservableExecutionHook.onCreate
Artifacts: Maven Central
0.18.0
This release takes us a step closer to 1.0 by completing some of the remaining work on the roadmap.
Scheduler
The first is simplifying the Scheduler API.
The Scheduler API is now simplified to this:
class Scheduler {
public abstract Worker createWorker();
public int parallelism();
public long now();
public abstract static class Worker implements Subscription {
public abstract Subscription schedule(Action0 action, long delayTime, TimeUnit unit);
public abstract Subscription schedule(Action0 action);
public Subscription schedulePeriodically(Action0 action, long initialDelay, long period, TimeUnit unit);
public long now();
}
}
This is a breaking change if you have a custom Scheduler
implementation or use a Scheduler
directly. If you only ever pass in a Scheduler
via the Schedulers
factory methods, this change does not affect you.
Additionally, the ExecutionScheduler
was removed because a general threadpool does not meet the requirements of sequential execution for an Observable
. It was replaced with rx.schedulers.EventLoopScheduler
which is the new default for Schedulers.computation()
. It is a pool of event loops.
rx.joins
The rx.joins
package and associated when
, and
and then
operators were moved out of rxjava-core into a new module rxjava-joins. This is done as the rx.joins API was not yet matured and is not going to happen before 1.0. It was determined low priority and not worth blocking a 1.0 release. If the API matures inside the separate module to the point where it makes sense to bring it back into the core it can be done in the 1.x series.
Deprecation Cleanup
This releases removes many of the classes and methods that have been deprecated in previous releases. Most of the removed functionality was migrated in previous releases to contrib modules such as rxjava-math, rxjava-async and rxjava-computation-expressions.
A handful of deprecated items still remain but can not yet be removed until all internal operators are finished migrating to using the lift
/Subscriber
design changes done in 0.17.0.
The full list of changes in 0.18.0:
- Pull 1047 Scheduler Simplification
- Pull 1072 Scheduler.Inner -> Scheduler.Worker
- Pull 1053 Deprecation Cleanup
- Pull 1052 Scheduler Cleanup
- Pull 1048 Remove ExecutorScheduler - New ComputationScheduler
- Pull 1049 Move rx.joins to rxjava-joins module
- Pull 1068 add synchronous test of resubscribe after error
- Pull 1066 CompositeSubscription fix
- Pull 1071 Manual Merge of AsObservable
- Pull 1063 Fix bugs in equals and hashCode of Timestamped
- Pull 1070 OperationAny -> OperatorAny
- Pull 1069 OperationAll -> OperatorAll
- Pull 1058 Typo in javadoc
- Pull 1056 Add drop(skip) and dropRight(skipLast) to rxscala
- Pull 1057 Fix: Retry in Scala adaptor is ambiguous
- Pull 1055 Fix: Missing Quasar instrumentation on Observable$2.call
- Pull 1050 Reimplement the 'SkipLast' operator
- Pull 967 Reimplement the 'single' operator
Artifacts: Maven Central
0.17.6
- Pull 1031 Fix NPE in SubjectSubscriptionManager
- Pull 1030 Benchmarking: Add JMH benchmark for ReplaySubject
- Pull 1033 isolate subscriber used for retries, cleanup tests
- Pull 1021 OperatorWeakBinding to not use WeakReferences anymore
- Pull 1005 add toMap from Java Observable
- Pull 1040 Fixed deadlock in Subjects + OperatorCache
- Pull 1042 Kotlin M7 and full compatibility with 0.17.0
- Pull 1035 Scala cleanup
- Pull 1009 Android - Adding a new RetainedFragment example
- Pull 1020 Upgrade Gradle wrapper for Android samples to Gradle 1.11
- Pull 1038 rxjava-android: parameterize OperatorViewClick by concrete view type
Artifacts: Maven Central
0.17.5
- Pull 1010 Observable.unsafeSubscribe
- Pull 1015 Remove Redundant protectivelyWrap Method
- Pull 1019 Fix: retry() never unsubscribes from source until operator completes
Artifacts: Maven Central
0.17.4
This release adds a new contrib module with a Scheduler
supporting lightweight threads or "fibers" via Quasar.
- Pull 990 Quasar Lightweight Threads/Fibers Contrib Module
- Pull 1012 SerializedObserver: Removed window between the two synchronized blocks
Artifacts: Maven Central
0.17.3
0.17.2
- Pull 963 A more robust JMH benchmarking set-up
- Pull 964 SubjectSubscriptionManager fix.
- Pull 970 Notifications for the allocation averse.
- Pull 973 Merge - Handle Bad Observables
- Pull 974 TestSubject, TestObserver and TestScheduler Improvements
- Pull 975 GroupBy & Time Gap Fixes
- Pull 976 parallel-merge unit test assertions
- Pull 977 Dematerialize - handle non-materialized terminal events
- Pull 982 Pivot Operator
- Pull 984 Tests and Javadoc for Pivot
- Pull 966 Reimplement the ElementAt operator and add it to rxjava-scala
- Pull 965 BugFix: Chain Subscription in TimeoutSubscriber and SerializedSubscriber
- Pull 986 Fix SynchronizedObserver.runConcurrencyTest
- Pull 987 Fix Non-Deterministic Pivot Test
- Pull 988 OnErrorFailedException
Artifacts: Maven Central
0.17.1
- 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
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:
- 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.
- 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...
0.17.0 Release Candidate 7
Pre-release of 0.17.0 for testing. See details at #802.