Talking about the new features of RxJava and 2.0

Introduction

Speaking of RxJava, I believe that many Android developers will not be unfamiliar. As a well-known responsive programming library, it has gradually become popular since the year before last, and has been widely introduced and circulated by many Android developers from a niche to a GitHub repository. As of this writing, there are already 16,400+ stars. . There are even some big cows who have written RxJava adaptation libraries for Android, such as
  • RxAndroid
  • RxBinding
  • RxLifecycle

Why RxJava is so popular with Android developers. I think for two reasons.
1. Asynchronous
2. Chained operation

Asynchronous

Friends who are familiar with Android threads know that Android's UI drawing and event response are on the main thread. In order to ensure the smoothness of the interface, many time-consuming operations such as reading and writing databases, reading When writing files and requesting the network, we will move them to the asynchronous thread to complete, and then call back to the main thread. Of course, after 4.0, the main thread is not allowed to request the network directly.

In the past, when there was no RxJava, developers generally completed these tasks through AsyncTask, Thread, or better yet, through the thread pool. With RxJava, you can switch threads at will with a simple sentence, which is not too comfortable.

The most typical Observable class in RxJava provides two functions, subscribeOn and observeOn. The former can switch the thread when it is being observed (if the thread that emits data is not rigorous enough, the data is not necessarily emitted when observing, especially when the developer customizes OnSubscribe), and the latter can switch the thread when the data is consumed.

Here's an example of switching threads:
Log.i("debug", Thread.currentThread().getName());
Observable.empty()
        .doOnCompleted(new Action0() {
            @Override
            public void call() {
                Log.i("debug", Thread.currentThread().getName());
            }
        })
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .doOnCompleted(new Action0() {
            @Override
            public void call() {
                Log.i("debug", Thread.currentThread().getName());
            }
        })
        .subscribe();

Here we don't have any data, we just emit an onComplete, but in the code for switching threads, we add additional code to be executed when onComplte, the output is as follows:
quote

08-27 10:47:41.173 6741-6741/com.dieyidezui.rxjavademo I/debug: main
08-27 10:47:41.201 6741-6762/com.dieyidezui.rxjavademo I/debug: RxIoScheduler-2
08-27 10:47:41.217 6741-6741/com.dieyidezui.rxjavademo I/debug: main

This is just a simple example, RxJava provides many convenient operators for us to use, such as map, filter, flatMap, merge, concat, etc. It can be seen that it is really helpful to our programming efficiency when used proficiently. Especially the MVP mode, RxJava combined with it can be described as "a match made in heaven".

Chained operation The code demonstrated by the author

above is actually a typical use of RxJava:
  • transmit data source
  • Intermediate operation
  • process result

The intermediate operations include many usages, such as switching threads, transforming data, and so on.

Why do I say chained operations are fine. First, chain logic replaces deep callback logic, which is easy to write and less prone to bugs. Second, RxJava provides many operators for processing data as a whole, which is very useful. Third, with the lambda expression of Java8, the code is short and elegant.

Well, that concludes our introduction to RxJava. There will be special articles after advanced usage and principle analysis. For students who are not familiar with RxJava, it is recommended to take a look at the official wiki first. Link: https://github.com/ReactiveX/RxJava/wiki

RxJava2.0 The

day before yesterday, RxJava finally released the 2.0 RC1 version. The author who has been paying attention to this immediately went in and tried it. Combined with the official introduction, the author summarizes and translates some similarities and differences with 1.x to share with you.

Package name and MAVEN dependencies The

first thing to say is that RxJava 2 and 1 are independent of each other. Therefore, the dependencies of the package name and maven are also different, just like OkHttp 3 and 2.
The dependency of RxJava 2.x is the new io.reactivex.rxjava2:rxjava:2.xy, and the classes are under the io.reactivex package name, not rx anymore.

Interface changes

RxJava2 is completed in accordance with the Reactive Streams Specification, and the new features depend on the four basic interfaces it provides. respectively
  • Publisher
  • Subscriber
  • Subscription
  • Processor

The new implementation of Flowable and Observable

is called Flowable, and the old Observable remains. Because in RxJava1.x, there are many events that cannot be properly backpressured, thus throwing MissingBackpressureException.

For a simple example, observeOn in RxJava1.x, because it switches the thread of the consumer, so the internal implementation uses a queue to store events. The default buffersize size in Android is 16, so when consumption is slower than production, and the number of queues accumulates to more than 16, MissingBackpressureException will be thrown. It is difficult for beginners to understand why this happens, making the learning curve extremely steep.

In 2.0, Observable no longer supports back pressure, while Flowable supports non-blocking back pressure. And the specification requires that all operators enforce backpressure support. Fortunately, most of the operators in Flowable are similar to the old Observable.

Single, Completable

Single and Completable all redesigned the interface based on the new idea of ​​Reactive Streams, mainly the interface of consumers, and now they are like this:
interface SingleObserver<T> {
    void onSubscribe(Disposable d);
    void onSuccess(T value);
    void onError(Throwable error);
}

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

Subscriber

compare Subscriber:
public interface Subscriber<T> {
    public void onSubscribe(Subscription s);
    public void onNext(T t);
    public void onError(Throwable t);
    public void onComplete();
}

We will find that the difference from the previous one is that there is an onSubscribe method. Subscription is as follows:

Subscription
public interface Subscription {
    public void request(long n);
    public void cancel();
}

Friends who are familiar with RxJava 1.x can find that the new Subscription is more like a synthesis of the old Producer and Subscription. He can not only request data from the upstream, but also interrupt and release resources. And the old Subscription is renamed Disposable

Disposable because the name is occupied here.
public interface Disposable {
    void dispose();
    boolean isDisposed();
}

The biggest difference here is this onSubscribe. According to the Specification, this function must be the first to be called, and then a Subscription will be passed to the caller to organize the new backpressure relationship in this way. When we consume data, we can decide to request data by ourselves through the Subscription object.

This is where the above non-blocking backpressure can be explained. The old blocking back pressure is that according to the downstream consumption speed, the midstream can choose to block and wait for downstream consumption, and then request data from the upstream. The new non-blocking process does not have an intermediate blocking process, and the downstream decides how much to take, as well as back pressure strategies, such as discarding the latest, discarding the oldest, caching, throwing exceptions, etc.

The new calling method brought by the new interface is not the same as the old one. After subscribe, there will be no Subscription, which is now Disposable. In order to maintain backward compatibility, Flowable provides the subscribeWith method to return the current Subscriber object, and At the same time, DefaultSubscriber, ResourceSubscriber, DisposableSubscriber are provided, so that they provide the Disposable interface, which can complete the same code as before:
ResourceSubscriber<Integer> subscriber = new ResourceSubscriber<Integer>() {
    @Override
    public void onStart() {
        request(Long.MAX_VALUE);
    }

    @Override
    public void onNext(Integer t) {
        System.out.println(t);
    }

    @Override
    public void onError(Throwable t) {
        t.printStackTrace();
    }

    @Override
    public void onComplete() {
        System.out.println("Done");
    }
};

Flowable.range(1, 10).delay(1, TimeUnit.SECONDS).subscribe(subscriber);

subscriber.dispose();

Revoking the create method permission The most obvious problem

in RxJava 1.x is that create is too open, causing it to be abused by developers instead of learning to use the provided operators.

And users don't know enough about RxJava, which leads to various problems, such as back pressure, exception handling, etc.

Since the specification requires all operators to support backpressure, the new create adopts a conservative design, allowing users to implement the FlowableOnSubscribe interface, select a backpressure strategy, and then implement encapsulation to support backpressure internally. A simple example is as follows:
Flowable.create((FlowableEmitter<Integer> emitter) -> {
    emitter.onNext(1);
    emitter.onNext(2);
    emitter.onComplete();
}, BackpressureStrategy.BUFFER);

Functions can throw exceptions

The new ActionX and FunctionX method declarations have added a throws Exception, which brings obvious benefits. Now we can write:
Flowable.just("file.txt")
.map(name -> Files.readLines(name))
.subscribe(lines -> System.out.println(lines.size()), Throwable::printStackTrace);

which was not possible before, because Files.readLines(name) would explicitly Throws an IOException. This is more friendly to lambdas without having to try catch again.

Scheduler can directly schedule

. In the past, it was necessary to createWorker first, and use Worker object to shedule. Now you can use these methods directly in Scheduler:
public abstract class Scheduler {

    public Disposable scheduleDirect(Runnable task) { ... }

    public Disposable scheduleDirect(Runnable task, long delay, TimeUnit unit) { ... }

    public Disposable scheduleDirectPeriodically(Runnable task, long initialDelay,
        long period, TimeUnit unit) { ... }

    public long now(TimeUnit unit) { ... }

    // ... rest is the same: lifecycle methods, worker creation
}

This is a small optimization that is convenient for developers to use.

Some inheritances of Observable are incorporated into Flowable,

such as ConnectableObservable, BlockObservable, etc., so that such code can be written directly in Flowable:
List<Integer> list = Flowable.range(1, 100).toList().blockingFirst();

Other modifications

There are some modifications that ordinary developers don't care about:
  • The hook method has changed, now you can hook at runtime by providing an interface
  • Some operators marked @Beta and @Experimental in 1.x are now merged into the official release
  • Some class name changes due to changes in class structure

and other changes.

Conclusion

RxJava is a classic work of open source, and the author has always paid attention to it. In the follow-up, the author will continue to bring you the source code analysis and advanced use series of RxJava. Thank you for reading, and if you have any questions, please feel free to discuss.

ReferencesReactive

Streams specificationOfficial

wiki

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326184221&siteId=291194637