RxJava development essentials 2-why is it Observables?
Why is it Observables?
In the object-oriented architecture, developers are committed to creating a group of decoupled entities. In this way, entities can be tested, reused, and maintained without interfering with the entire system. Designing such a system brings about a tricky negative impact: maintaining consistency between related objects.
In the Smalltalk MVC Architecture, the first example of the creation mode is used to solve this problem. The user interface framework provides a way to separate the UI elements from the object containing data, and also provides a flexible way to maintain synchronization between them.
The observer model is one of the most famous design patterns in the design patterns-the basis for reusable object-oriented software written by the best-selling four-person group. It is a behavior pattern and provides a method to bind objects with one-to-many dependencies: that is, when an object changes, all objects dependent on it will be notified and updated automatically.
In this chapter, we will give an overview of the observer mode, how it is implemented, how it is extended using RxJava, what is Observable, and how Observables are associated with Iterables.
Observer Mode
Today, the observer pattern is one of the most common software design patterns. It is based on the concept of subject. Subject is a special object. When it changes, the objects stored by it will be notified. These objects are called Observers, which exposes a notification method and will be called when the subject status changes.
In the previous chapter, we see an example of an electronic form. Now we can expand this example to show a more complex scenario. Let's consider an electronic form that fills in account data. We can compare the data to a table, a 3D bar chart, or a pie chart. The meaning of each of them depends on the data to be displayed in the same group. Every observer is dependent on that subject and maintains all information.
The class of 3D bar chart, pie chart, table, and the class that maintains the data are completely decoupled: they are reused independently, but can work together. These classes are not clear to each other, but as they do: they know where to find the information they need to display, they also know that once the data changes, the class that needs to be updated is notified.
This figure describes the one-to-many relationship between Subject and Observer:
The figure above shows that a Subject provides services for three Observers. Obviously, there is no reason to limit the number of Observers: if necessary, a Subject can have an infinite number of Observers. When the subject status changes, each of these Observers will receive a notification.
When do you use the observer mode?
The observer mode is suitable for any of the following scenarios:
When your architecture has two entity classes, one dependent on the other, you want them to be independent or reused. When a changed object notifies those unknown objects associated with its own changes. When a changed object notifies those objects of no need to deduce the specific type. RxJava observer mode Toolkit
In the RxJava world, we have four roles:
* Observable
* Observer
* Subscriber
* Subjects
Observables and Subjects are two "production" entities, and Observers and Subscribers are two "consumption" entities.
Observable
When we execute some complicated tasks asynchronously, Java provides traditional classes such as Thread, Future, FutureTask, and CompletableFuture to deal with these problems. When the complexity increases, these solutions will become troublesome and difficult to maintain. Worst of all, they do not support chained calls.
RxJava Observables are designed to solve these problems. They are flexible and easy to use. They can also be chained and act on a single result program. What's more, they can also act on sequences. You can use Observable whenever you want to emit a Single Scalar Value, a series of values, or even infinite numeric streams.
The lifecycle of the Observable includes three events that are easier to compare with the Iterable lifecycle events. The following table shows how to associate Observable async/push with Iterable sync/pull.
Event |
Iterable (pull) |
Observable (push) |
Retrieve Data |
T next() |
onNext(T) |
Error detected |
throws Exception |
onError(Throwable) |
Complete |
!hasNext() |
onCompleted() |
When Iterable is used, the consumer obtains the value from the producer in synchronous mode, and the thread is in a blocking state before the value reaches. On the contrary, when Observable is used, the producer asynchronously pushes values to the observer, and these values are available at any time. This method is more flexible because, even if the value arrives in synchronous or asynchronous mode, the consumer can process it as needed in both scenarios.
To better reuse the Iterable interface, the RxJava Observable class extends the semantics of the GOF observer mode. Two new interfaces are introduced:
* OnCompleted () means that the notification observer Observable does not have more data.
* OnError () indicates that the observer has an error.
Hot Observables and cold Observables
From the perspective of the transmitter, there are two different Observables: hot and cold. A "hot" Observable typically starts to launch data as soon as it is created, therefore, all the observers that subsequently subscribe to it may accept data from a location in the middle of the sequence (some data are missed ). A "cold" Observable will wait until an observer subscribes to it to start transmitting data. Therefore, this observer can ensure that the whole data sequence is received.
Create an Observable
In the next section, we will discuss two methods provided by Observables to create an Observable.
Observable. create ()
The create () method enables developers to create an Observable from scratch. It requires an OnSubscribe object, which inherits Action1. When the observer subscribes to our Observable, it passes in as a parameter and executes the call () function.
Observable.create(new Observable.OnSubscribe(){ @Override public void call(Subscriber subscriber) { }});
Observable communicates with the observer by using the subscriber variable and calling its method according to the condition. Let's look at an example of "real world:
Observable
observableString = Observable.create(new Observable.OnSubscribe
() { @Override public void call(Subscriber observer) { for (int i = 0; i < 5; i++) { observer.onNext(i); } observer.onCompleted(); }});Subscription subscriptionPrint = observableString.subscribe(new Observer
() { @Override public void onCompleted() { System.out.println("Observable completed"); } @Override public void onError(Throwable e) { System.out.println("Oh,no! Something wrong happened!"); } @Override public void onNext(Integer item) { System.out.println("Item is " + item); }});
The example is intentionally written simply because, even if it is the first time you see RxJava operations, I want you to understand what will happen next.
We create a newObservable
It executes the for loop of five elements, one by one, and finally completes.
On the other hand, we subscribe to the Observable and return a subscribe
. Once we subscribe, we begin to accept integers and print them one by one. We don't know how many integers we want to accept. In fact, we do not need to know because we provide corresponding processing operations for each scenario:
* If we receive an integer, print it.
* If the sequence ends, a closed sequence is printed.
* If an error occurs, we will print an error message.
Observable. from ()
In the previous example, we created an integer sequence and fired them one by one. What if we already have a list? Can we launch them one by one without a for loop?
In the following example code, we create an Observable sequence from an existing list:
List
items = new ArrayList
();items.add(1);items.add(10);items.add(100);items.add(200);Observable
observableString = Observable.from(items);Subscription subscriptionPrint = observableString.subscribe(new Observer
() { @Override public void onCompleted() { System.out.println("Observable completed"); } @Override public void onError(Throwable e) { System.out.println("Oh,no! Something wrong happened!"); } @Override public void onNext(Integer item) { System.out.println("Item is " + item); }});
The output result is the same as the preceding example.
from()
You can create an Observable from a list or array, and upload each object from the list or array one by one, orFuture
Class to create the Observable and transmit the Future object.get()
The result value returned by the method. InputFuture
As a parameter, we can specify a timeout value. Observable will waitFuture
If no results are returned before the timeout, the Observable will triggeronError()
Method to notify the observer that an error has occurred.
Observable. just ()
If we already have a traditional Java function, what should we do if we want to convert it into an Observable? We can usecreate()
Method, as we have seen earlier, or we can use it as below to save a lot of template code:
Observable
observableString = Observable.just(helloWorld());Subscription subscriptionPrint = observableString.subscribe(new Observer
() { @Override public void onCompleted() { System.out.println("Observable completed"); } @Override public void onError(Throwable e) { System.out.println("Oh,no! Something wrong happened!"); } @Override public void onNext(String message) { System.out.println(message); }});
helloWorld()
The method is relatively simple, like this:
private String helloWorld(){ return "Hello World";}
In any case, it can be any function we want. In the previous example, once we create an Observable,just()
Execute the function. When we subscribe to Observable, it will output the returned value.
just()
Methods can be passed in one to nine parameters, which are transmitted in the order of input parameters.just()
The method can also accept lists or arrays, just likefrom()
Method, but it does not iterate the list to emit each value, it will launch the entire list. Generally, it is used when we want to launch a set of defined values. However, if our functions are not time-varying, we can use just to create a more organized and testability code base.
Last notejust()
After it emits a value, the Observable ends normally. In the example above, we will print two messages on the console: "Hello World" and "Observable completed ".
Observable. empty (), Observable. never (), and Observable. throw ()
When we need an Observable to stop transmitting data for no reason, we can useempty()
. We can usenever()
Create an Observable that does not transmit data and will never end. We can also usethrow()
Create an Observable that does not transmit data and ends with an error.
Subject = Observable + Observer
subject
It is a magic object. It can be both an Observable and an Observer: it serves as a bridge connecting the two worlds. A Subject can subscribe to an Observable, just like an observer, and it can transmit new data or pass the data it receives, just like an Observable. Obviously, as an Observable, the observer or other Subject can subscribe to it.
Once the Subject subscribes to the Observable, it will trigger the Observable to start launching. If the original Observable is "cold", this will affect the subscription of a "hot" Observable variable.
RxJava provides four different Subject types:
* PublishSubject
* BehaviorSubject
* ReplaySubject.
* AsyncSubject
PublishSubject
Publish is a basic subclass of Subject. Let's take a look at using PublishSubject to implement the traditional ObservableHello World
:
PublishSubject
stringPublishSubject = PublishSubject.create();Subscription subscriptionPrint = stringPublishSubject.subscribe(new Observer
() { @Override public void onCompleted() { System.out.println("Observable completed"); } @Override public void onError(Throwable e) { System.out.println("Oh,no!Something wrong happened!"); } @Override public void onNext(String message) { System.out.println(message); }});stringPublishSubject.onNext("Hello World");
In the previous example, we createdPublishSubject
, Usecreate()
Method to launchString
Value, And then we subscribe to PublishSubject. At this time, there is no data to be sent, so our observer can only wait, there is no blocking thread, and there is no resource consumption. We are ready to receive values from subject at any time. If the subject has no transmit value, our observer will remain waiting. Again, there is no need to worry: The Observer knows what to do in each scenario, and we don't have to worry about when it is responsive: the system will respond. We don't care when it will respond. We only care about what to do when it responds.
The last line of code shows the manual launch string "Hello World", which triggers the observer'sonNext()
Method, Let's print "Hello World" Information on the console.
Let's look at a more complex example. In other words, we haveprivate
Declared Observable, which cannot be accessed externally. The emission value of Observable within its lifecycle. We don't need to care about these values, but we only care about their end.
First, we create a new PublishSubject to respond to itsonNext()
And can be accessed externally.
final PublishSubject
subject = PublishSubject.create();subject.subscribe(new Observer
() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Boolean aBoolean) { System.out.println("Observable Completed"); }});
Then, we create a "private" Observable, which can be accessed only by subject.
Observable.create(new Observable.OnSubscribe
() { @Override public void call(Subscriber subscriber) { for (int i = 0; i < 5; i++) { subscriber.onNext(i); } subscriber.onCompleted(); }}).doOnCompleted(new Action0() { @Override public void call() { subject.onNext(true); }}).subscribe();
Observable.create()
The method contains a familiar for loop that emits numbers.doOnCompleted()
The method specifies what to do when the Observable ends: enable true on the subject. Finally, we subscribed to the Observable. Obviously, emptysubscribe()
The call is only to enable the Observable, without having to worry about any value that has been sent or the event or error event. For this example, we need it like this.
In this example, we create an object that can be connected to Observables and be observed at the same time. This is extremely useful when we want to create independent, abstract, or easier observations for public resources.
BehaviorSubject
To put it simply, BehaviorSubject first sends the latest data object (or initial value) before subscription to its subscriber, and then normally sends the subscribed data stream.
BehaviorSubject
behaviorSubject = BehaviorSubject.create(1);
In this short example, we create a BehaviorSubject that can emit an Integer. Every time an Observes subscribes to it, it transmits the latest data, so it requires an initial value.
ReplaySubject
ReplaySubject caches all the data it subscribes to and resends it to any observer who subscribes to it:
ReplaySubject
replaySubject = ReplaySubject.create();
AsyncSubject
When the Observable is complete, AsyncSubject only publishes the last data to each subscribed observer.
AsyncSubject
asyncSubject = AsyncSubject.create();
Summary
In this chapter, we learned what is the observer mode, why is Observables so important in today's programming scenarios, and how to create Observables and subjects.
In the next chapter, we will create the first Android Application Based on RxJava, learn how to retrieve data to populate the listview, and explore how to create a responsive UI Based on RxJava.