A simple comparison between RxJava2 and RxJava1

Keywords: Java Android

Preface:

RxJava 2.0 was officially released on October 29, 2016, and I took the time to study its related features. Strike while the iron is hot. In this article, a brief summary of the use of RxJava 2.0 is given.

Before reading this article, you need to grasp the basic concepts of RxJava 1.0. If you have never touched RxJava, please click Here

RxJava 2.0 VS RxJava 1.0:

1. RxJava 2.0 no longer supports null values, and if a null is passed in, a NullPointerException will be thrown.

    Observable.just(null);
    Single.just(null);
    Flowable.just(null);
    Maybe.just(null);
    Observable.fromCallable(() -> null)
            .subscribe(System.out::println, Throwable::printStackTrace);
    Observable.just(1).map(v -> null)
            .subscribe(System.out::println, Throwable::printStackTrace);

2. All function/action/Consumer interfaces in RxJava 2.0 are designed to throw Exception, which solves the problem that compiler exceptions need to be converted.

3. In RxJava 1.0, Observable can not support backpressure very well. In RxJava 2.0, Oberservable is completely implemented to support backpressure, while Flowable is added to support backpressure. (For the concept of back pressure, please refer to my original English version of ReativeX. Chinese translation)

 

I. Observable

RxJava 1.0 has four basic concepts: Observable, Observer, subscribe, and event. Observable and Observer implement the subscribe relationship through subscribe() method, so that Observable can send out events to notify Observer when needed.

            

Based on the above concepts, the basic implementation of RxJava 1.0 has three main points:

Step 1: Create Observer

Observer is the observer, which decides what will happen when an event triggers. The implementation of Observer interface in RxJava:

Observer<String> observer = new Observer<String>() {
    @Override
    public void onNext(String s) {
        Log.d(tag, "Item: " + s);
    }

    @Override
    public void onCompleted() {
        Log.d(tag, "Completed!");
    }

    @Override
    public void onError(Throwable e) {
        Log.d(tag, "Error!");
    }
};

In addition to the Observer interface, RxJava has built-in an abstract class that implements Observer: Subscriber. Subscriber extends the Observer interface, but their basic usage is exactly the same:

Subscriber<String> subscriber = new Subscriber<String>() {
    @Override
    public void onNext(String s) {
        Log.d(tag, "Item: " + s);
    }

    @Override
    public void onCompleted() {
        Log.d(tag, "Completed!");
    }

    @Override
    public void onError(Throwable e) {
        Log.d(tag, "Error!");
    }
};

Step 2: Create Observable

Observable is the observee who decides when and what events to trigger. RxJava uses the create() method to create an Observable and defines event triggering rules for it:

Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        subscriber.onNext("Hello");
        subscriber.onNext("Hi");
        subscriber.onNext("Aloha");
        subscriber.onCompleted();
    }
});

Step 3: Subscribe (Subscribe)

After creating Observable and Observer, the subscrbe() method is used to connect them, and the whole chain can work. The code form is simple:

observable.subscribe(observer);
// Or:
observable.subscribe(subscriber);

 

However, in 2.0, the familiar Subscrber disappeared, replaced by Observable Emitter, commonly known as the transmitter. In addition, since there is no Subscrber, we need to use Observer to create observers. Observer is not the one we are familiar with, and the parameters of Disosable that it calls back are even more difficult to understand.


Step 1: Initialize an Observable

       Observable<Integer> observable=Observable.create(new ObservableOnSubscribe<Integer>() {

            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                e.onNext(1);
                e.onNext(2);
                e.onComplete();
            }
        });

 

Step 2: Initialize an Observer


        Observer<Integer> observer= new Observer<Integer>() {

            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onNext(Integer value) {


            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {
            }
        }

 

Step 3: Establish a subscription relationship

    observable.subscribe(observer); //Establish a subscription relationship

 

It is not difficult to see that there are some differences between RxJava 1.0 and RxJava 1.0. First, when creating Observable, the callback is Observable Emitter, literally the emitter, which is used to transmit data (onNext()) and notification (onError()/onComplete()). Secondly, there is an additional callback method onSubscribe() in the created Observer, passing the parameter Disposable.

Observable Emitter: Emitter means a transmitter, which is used to emit events. It can emit three types of events. By calling onNext(T value), onComplete() and onError (Throwable) of emitter, the next event, complete event and error event can be emitted respectively.

Disposable: The literal meaning of this word is disposable goods, which can be discarded when used up. So how to understand it in RxJava? Corresponding to the example of the pipe above, we can understand it as an organ between two pipes. When its dispose() method is called, it will cut off the two pipes and cause the downstream events not to be received, which is equivalent to Subsciption.

Note: Calling dispose() does not cause the upstream to stop sending events, and the upstream will continue sending the remaining events.

Let's take an example. Let's send 1, 2, complete, 4 upstream in turn. After receiving the second event downstream, cut off the water pipe and see the results of the operation.

        Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                Log.d(TAG, "emit 1");
                emitter.onNext(1);
                Log.d(TAG, "emit 2");
                emitter.onNext(2);
                Log.d(TAG, "emit 3");
                emitter.onNext(3);
                Log.d(TAG, "emit complete");
                emitter.onComplete();
                Log.d(TAG, "emit 4");
                emitter.onNext(4);
            }
        }).subscribe(new Observer<Integer>() {
            private Disposable mDisposable;
            private int i;

            @Override
            public void onSubscribe(Disposable d) {
                Log.d(TAG, "subscribe");
                mDisposable = d;
            }

            @Override
            public void onNext(Integer value) {
                Log.d(TAG, "onNext: " + value);
                i++;
                if (i == 2) {
                    Log.d(TAG, "dispose");
                    mDisposable.dispose();
                    Log.d(TAG, "isDisposed : " + mDisposable.isDisposed());
                }
            }

            @Override
            public void onError(Throwable e) {
                Log.d(TAG, "error");
            }

            @Override
            public void onComplete() {
                Log.d(TAG, "complete");
            }
        });

The results are as follows:

12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: subscribe
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 1
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: onNext: 1
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 2
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: onNext: 2
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: dispose
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: isDisposed : true
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 3
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit complete
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 4

From the running results, we can see that after receiving the onNext 2 event, the water pipe was cut off, but the upstream still sent 3, complete, 4 events, and the upstream did not stop because it sent onComplete. At the same time, you can see that the downstream onSubscibe() method was first invoked.

Disposable is more useful than that. When we talk about thread scheduling later, we will find its importance. With further explanation, we will find it in more places.

In addition, other simplified subscription methods are still retained in RxJava 2.x, and we can choose the corresponding simplified subscription according to our needs. It's just that the incoming object is changed to Consumer. `

   Disposable disposable = observable.subscribe(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) throws Exception {
                  //Receive data items here
            }
        }, new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {
              //Receive onError here
            }
        }, new Action() {
            @Override
            public void run() throws Exception {
              //Receive onComplete here.
            }
        });

 

Unlike RxJava 1.0, RxJava 2.0 does not have a series of Action/Func interfaces. Instead, it has a functional interface similar to Java 8 named, as follows:
 
Action is similar to Action0 in RxJava 1.0, except that Action allows exceptions to be thrown.

public interface Action {
    /**
     * Runs the action and optionally throws a checked exception
     * @throws Exception if the implementation wishes to throw a checked exception
     */
    void run() throws Exception;
}

 

Consumer is the consumer, which receives a single value, BigConsumer receives two values, Function is used to transform objects, Predicate is used to judge. Most of these interface names refer to Java 8, and you should know the meaning of the new features of Java 8, so I won't repeat them here.

Thread Scheduling

With regard to thread switching, RxJava 1.x and RxJava 2.x have the same idea of implementation. Here's a brief look at the relevant source code.

1. subscribeOn

Like RxJava 1.x, subscribeOn is used to specify the threads that occur when subscribe() occurs. From the source point of view, internal thread scheduling is achieved through Observable SubscribeOn.

   public final Observable<T> subscribeOn(Scheduler scheduler) {
        ObjectHelper.requireNonNull(scheduler, "scheduler is null");
        return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
    }

 

The core source code of Observable SubscribeOn is in subscribe Actual method. After wrapping Observer with SubscribeOn Observer by proxy, set Disposable to switch subscribe to Scheduler thread.

    @Override
    public void subscribeActual(final Observer<? super T> s) {
        final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);

        s.onSubscribe(parent); //Callback Disposable

        parent.setDisposable(scheduler.scheduleDirect(new Runnable() { //Setting `Disposable'`
            @Override
            public void run() {
                source.subscribe(parent); //Make Observable subscribe happen in Scheduler threads
            }
        }));
    }

 

2. observeOn

The observeOn method is used to specify the thread in which the downstream Observer callback occurs.

    public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
         //..
         //Verification Security
        return RxJavaPlugins.onAssembly(new ObservableObserveOn<T>(this, scheduler, delayError, bufferSize));
    }

 

The main implementation is subscribe Actual in Observable Observer On. It can be seen that, unlike subscribe On, subscribe operations are not all switched to Scheduler, but through the cooperation of Observer OnSubscriber and Scheduler, through schedule() to switch the threads occurring in downstream Observer callback, which is implemented with RxJava 1.0. Almost the same. The source code for Observer On Subscriber is no longer described here.

    @Override
    protected void subscribeActual(Observer<? super T> observer) {
        if (scheduler instanceof TrampolineScheduler) {
            source.subscribe(observer);
        } else {
            Scheduler.Worker w = scheduler.createWorker();

            source.subscribe(new ObserveOnSubscriber<T>(observer, w, delayError, bufferSize));
        }
    }

 

Flowable

Flowable is a new class in RxJava 2.0 that is designed to deal with Backpressure problems, but it is not a new concept introduced in RxJava 2.0. The so-called back pressure, that is, the producer's speed is faster than the consumer's speed brings about problems, such as the common click events in Android, click too fast will cause the effect of two clicks.  

We know that backpressure control in RxJava 1.0 is done by Observable, using the following:

  Observable.range(1,10000)
            .onBackpressureDrop()
            .subscribe(integer -> Log.d("JG",integer.toString()));

In RxJava 2.0, it is separated and named Flowable. Therefore, the original Observable is no longer capable of backpressure processing.  
Through Flowable, we can customize the backpressure processing strategy.  

/**
 * Represents the options for applying backpressure to a source sequence.
 */
public enum BackpressureStrategy {
    /**
     * OnNext events are written without any buffering or dropping.
     * Downstream has to deal with any overflow.
     * <p>Useful when one applies one of the custom-parameter onBackpressureXXX operators.
     */
    MISSING,
    /**
     * Signals a MissingBackpressureException in case the downstream can't keep up.
     */
    ERROR,
    /**
     * Buffers <em>all</em> onNext values until the downstream consumes it.
     */
    BUFFER,
    /**
     * Drops the most recent onNext value if the downstream can't keep up.
     */
    DROP,
    /**
     * Keeps only the latest onNext value, overwriting any previous value if the
     * downstream can't keep up.
     */
    LATEST
}

Examples of testing Flowable are as follows:

  Flowable.create(new FlowableOnSubscribe<Integer>() {

            @Override
            public void subscribe(FlowableEmitter<Integer> e) throws Exception {

                for(int i=0;i<10000;i++){
                    e.onNext(i);
                }
                e.onComplete();
            }
        }, FlowableEmitter.BackpressureStrategy.ERROR) //Specify backpressure handling strategy, throw exception
                .subscribeOn(Schedulers.computation())
                .observeOn(Schedulers.newThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Exception {
                        Log.d("JG", integer.toString());
                        Thread.sleep(1000);
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.d("JG",throwable.toString());
                    }
                });

 

Or it can be controlled in a way similar to RxJava 1.0.

  Flowable.range(1,10000)
                .onBackpressureDrop()
                .subscribe(integer -> Log.d("JG",integer.toString()));

 

One thing to note is that Flowable doesn't start sending data as soon as subscriptions are made, but rather wait until Subscription.request() is executed to start sending data. Of course, using the simplified subscribe subscription method specifies Long.MAX_VALUE by default. Examples of manual designation are as follows:


        Flowable.range(1,10).subscribe(new Subscriber<Integer>() {
            @Override
            public void onSubscribe(Subscription s) {
                s.request(Long.MAX_VALUE);//Set the number of requests
            }

            @Override
            public void onNext(Integer integer) {

            }

            @Override
            public void onError(Throwable t) {

            }

            @Override
            public void onComplete() {

            }
        });

 

III. Single

Unlike Single Subscriber in RxJava 1.0, Single Observer in RxJava 2.0 has an additional callback method, onSubscribe.

interface SingleObserver<T> {
    void onSubscribe(Disposable d);
    void onSuccess(T value);
    void onError(Throwable error);
}

 

IV. Completable

With Single, Completable has also been redesigned as Reactive-Streams architecture, and Comppletable Subscriber of RxJava 1.0 has been changed to Completable Observer with the following source code:

interface CompletableObserver<T> {
    void onSubscribe(Disposable d);
    void onComplete();
    void onError(Throwable error);
}

 

V. Subject/Processor

Processor and Subject work the same way. Regarding the Subject section, RxJava 1.0 is not significantly different from RxJava 2.0 in usage, which is not covered here. Processor is a new addition to RxJava 2.0, inherited from Flowable, so it supports backpressure control. Subject does not support backpressure control. Use as follows:

    //Subject
    AsyncSubject<String> subject = AsyncSubject.create();
    subject.subscribe(o -> Log.d("JG",o));//three
    subject.onNext("one");
    subject.onNext("two");
    subject.onNext("three");
    subject.onComplete();

    //Processor
    AsyncProcessor<String> processor = AsyncProcessor.create();
    processor.subscribe(o -> Log.d("JG",o)); //three
    processor.onNext("one");
    processor.onNext("two");
    processor.onNext("three");
    processor.onComplete();

 

6. Operators

Regarding operators, RxJava 1.0 and RxJava 2.0 are mostly consistent in terms of naming and behavior, with emphasis on subscribe With operators and compose operators.

1. subscribeWith

In RxJava 2.0, subscribe operation no longer returns Subscription, which is now Disposable. To maintain backward compatibility, Flowable provides subscribe With method to return the current observer Subscriber object, and also provides DefaultSubsriber, ResourceSubscriber, Disposable Subscriber interfaces to enable them to Disposable objects are provided to manage their life cycles.

2. compose

RxJava 1.0 Usage:

	private static <T> Observable.Transformer<T, T> createIOSchedulers() {
		return new Observable.Transformer<T, T>() {
			@Override
			public Observable<T> call(Observable<T> tObservable) {
				return tObservable.subscribeOn(Schedulers.io())
						.unsubscribeOn(AndroidSchedulers.mainThread())
						.observeOn(AndroidSchedulers.mainThread());
			}
		};
	}

	public static <T> Observable.Transformer<JsonResult<T>,T> applySchedulers() {
		return createIOSchedulers();
	}
Action1<Integer> onNext = null;
String[] items = { "item1", "item2", "item3" };
Subscription subscription = Observable.from(items)
                                      .compose(RxUtil.<String>applySchedulers())
                                      .map(new Func1<String, Integer>() {
                                                  @Override public Integer call(String s) {
                                                      return Integer.valueOf(s);
                                                  }
                                              })
                                      .subscribe(onNext);

RxJava 2.0 Usage:

    public static <T> ObservableTransformer<T, T> io2MainObservable() {
        return new ObservableTransformer<T, T>() {
            @Override
            public ObservableSource<T> apply(Observable<T> upstream) {
                return upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
            }
        };
    }

	public static <T> ObservableTransformer<T, T> applySchedulers() {
		return io2MainObservable();
	}
Consumer<Integer> onNext = null;
String[] items = { "item1", "item2", "item3" };
Disposable disposable = Observable.fromArray(items)
                                  .compose(RxUtil.<String>applySchedulers())
                                  .map(new Function<String, Integer>() {
                                              @Override public Integer apply(String s) throws Exception {
                                                  return Integer.valueOf(s);
                                              }
                                          })
                                  .subscribe(onNext);

It can be noted that RxJava 1.0 implements the rx.Observable.Transformer interface, which inherits from Func1<Observable<T>, Observable<R> and 2.0 inherits from io.reactivex.Observable Tansformer<Upstream, Downstream> as a separate interface.

In addition, RxJava 2.0 also provides a Flowable Transformer interface for the compose operator under Flowable, using the following:

    public static <T> FlowableTransformer<T, T> io2MainFlowable() {
        return new FlowableTransformer<T, T>() {
            @Override
            public Publisher<T> apply(Flowable<T> upstream) {
                return upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
            }
        };
    }

	public static <T> FlowableTransformer<T, T> applySchedulers() {
		return io2MainFlowable();
	}

Consumer<Integer> onNext = null;
Disposable disposable = Flowable.create(new FlowableOnSubscribe<Integer>() {

            @Override
            public void subscribe(FlowableEmitter<Integer> e) throws Exception {

                for(int i=0;i<10000;i++){
                    e.onNext(i);
                }
                e.onComplete();
            }
        }, FlowableEmitter.BackpressureStrategy.ERROR) //Specify backpressure handling strategy, throw exception
        .compose(RxUtil.<String>applySchedulers())
        .subscribe(onNext);

Posted by mailjol on Sun, 07 Jul 2019 11:55:31 -0700