This article starts: http://prototypez.github.io/2016/04/10/rxjava-common-mistakes-1/
Reprint please indicate the source
Ready to write this article time to see the next RxJava on Github has 12000+ a star, visible popularity, their use of RxJava has been a short period of time. Originally in the community to RxJava a piece of praise, began to use RxJava to replace some of the project's simple asynchronous request, until later began to contact some advanced play, this middle reading code plus their own tread on the pit, slowly accumulated some experience, many are novice easy to make mistakes and RxJava A place that is easily misunderstood. This article is not finished, so I intend to write a series, this article is the first article of this series.
Careful use of subject
Subject
Observable
It is also Observer
, because of itself, that it Observer
can be called from anywhere in the project onNext
(as long as the Subject reference is available). It looks good, doesn't it? Compared Observable.create
to, Observable.from
Observable.just
more convenient, the three factory methods have a feature, that is, the built-in Observable emission elements are determined, even in many cases, the elements to be emitted as constants in the compilation period can be determined. When I first started learning these little examples of getting started, I was thinking about how simple it is: events that interact with the UI, changes in the network type of the mobile device (WiFi and cellular switching), the arrival of the server push message, when the event occurs and the number of times it is generated, is known at run time. How can you use these factory methods to simply emit a few fixed values.
Until I met Subject
. I can start by creating a first element that doesn't emit Observable
( Subject
Observable
the subclass), and create the corresponding subscription at the same time, and Subscriber
Observable
then when I think of a ready time, call this Subject
object's onNext
method to its Subscriber
emitting elements. The logic is concise and flexible enough, and the code looks like this:
PublishSubject<String> subject = PublishSubject.create();subject.map(String::length) .subscribe(System.out::println);...// 在某个Ready的时机subject.onNext("Puppy");...// 当某个时刻subjcet已经完成了使命subject.onCompleted();
Using Subject may lead to missed events that are really concerned
At present, everything is logical, contrast Observable
, Subject
obvious advantages, can be on demand at the right time to launch elements, it seems to be Subject
more able to meet the needs of daily tasks, more radical, simply use Subject
to replace all the Observable
bar. In fact, I did, but soon I got into trouble. For example, the code is as follows:
publishsubject<string> operation = Publishsubject.create (); operation. Subscribe (NewSubscriber<string> () {@Override Public void oncompleted() {System.out.println ("Completed"); }@Override Public void OnError(Throwable e) { }@Override Public void OnNext(String s) {System.out.println (s); }}); Operation.onnext ("Foo"); Operation.onnext ("Bar"); operation.oncompleted ();
This code is very simple, as expected, its output is:
FooBarcompleted
Change it a little bit, using the RxJava Scheduler to Scheduler
specify operation
objects to emit elements from the IO thread, the code is as follows (the code in this article is main
run from the start of the function):
publishsubject<string> operation = Publishsubject.create (); operation. Subscribeon (Schedulers.io ()). Subscribe (NewSubscriber<string> () {@Override Public void oncompleted() {System.out.println ("Completed"); }@Override Public void OnError(Throwable e) { }@Override Public void OnNext(String s) {System.out.println (s); }}); Operation.onnext ("Foo"); Operation.onnext ("Bar"); operation.oncompleted (); Sleep ( -);
The result of the actual output of the above code is:
completed
In the above code, in addition to the scheduler, and finally added a line of code to keep the current thread dormant for 2 seconds, because the operation
object is changed from the IO thread after the element is fired, the main thread is running to the last line directly exited, causing the entire process to end, the IO thread has not started firing elements, so this 2 seconds is used to wait for the IO thread to start up and finish the task.
The changed code is not received Foo
and Bar
, if the last line is sleep(2000)
removed, then the Console will not output anything. This is the first reason why we need to be cautious Subject
: using Subject can lead to missed events that are really concerned .
In RxJava
, Observable
can be divided into Hot Observable
with Cold Observable
, citing "learning reactive programming with Java 8" in an image of the metaphor (translated meaning):
We can assume that each Cold Observable
time a subscription is sent for each of the Subscriber
elements that are available for use, and always in the Hot Observable
running state, when it is running, the element is emitted to its subscribers (sending broadcasts, events), and we can liken it to Hot Observable
a radio station, Listeners listen to the station at some point and start to hear the show and then the show, but can't hear the radio show, and Cold Observable
like a music CD, people buy CDs time may be a gap before and after, but listen to the CD is from the first track start playing. In other words, the same CD, everyone hears the same content, regardless of the listening time early or late.
Subjcet
Belong Hot Observable
to it. Cold Observable
can be converted to Hot Observable
, but not in turn. Back to explain why the above example is only output completed
: Because the operation
object emitting element of the thread is assigned to the IO thread, the corresponding subscriber also work on the IO thread, and the IO thread is called by Scheduler for the first time, not yet up (initializing), The first two elements of the launch, the Foo
Bar
main thread, the main thread of these two elements to the IO thread forwarding process because the IO thread has not been up, it was discarded (radio station even if there is no listener, can still broadcast). complete
signal is very special, in Reactive X
the design, the signal priority is very high, so can always be a priority to capture, but this is another topic.
So Subject
when using, we must carefully design the program, to ensure that the message is sent when the time is Subscriber
ready, otherwise we can easily miss the events we care about, when the code is faced with refactoring in the future, other programmers must know this logic, otherwise it is easy to introduce Bug. If we do not want to miss any events, then we should use as much as possible Cold Observable
, the above example if the operation
object used Observable.just
, Observable.from
to construct, there is no such a problem.
In fact, a missed event typically occurs under critical conditions, such as I just declared one Subscriber
and want to send an event to it immediately. It is best not to use it at this time Subject
Observable.create(OnSubscribe)
. The problem code above is changed to the following, it will work properly:
observable<string> operation = Observable.create (subscriber, {Subscriber.onnext ("Foo"); Subscriber.onnext ("Bar"); Subscriber.oncompleted ();}); O Peration. Subscribeon (Schedulers.io ()). Subscribe (NewSubscriber<string> () {@Override Public void oncompleted() {System.out.println ("Completed"); }@Override Public void OnError(Throwable e) { }@Override Public void OnNext(String s) {System.out.println (s); }}); Sleep ( -);
Subjcet is not thread-safe
The second problem to use is that Subject
it is not thread-safe , and if the method that calls it on different threads onNext
is likely to cause a race condition (race conditions), we should try to avoid the occurrence of this situation, Because unless you write enough detailed comments in your code, programmers who maintain this code in the future are likely to step on the hole somehow. If you think you really need to use Subject
it, then turn it into a SerializedSubject
thread-safe way to ensure that multiple threads call the method at the same time onNext
.
SerializedSubject<Integer,Integer> subject = PublishSubject.<Integer>create().toSerialized();
Subject the sending of events becomes unpredictable
The last reason we should be cautious Subject
is that it makes the sending of events unpredictable. Since Observable.create
The example used above has been given, look at the other two factory methods Observable.just
and Observable.from
examples:
Observable<String> values = Observable.just("Foo""Bar");Observable myObservable = Observable.from(new String[]{"Foo","Bar"});
Either Observable.create
, Observable.from
or Observable.just
, there is Cold Observable
a significant advantage to the fact that the source of the data is predictable, and I know what data will be sent and what type of data it is. But Subject
not the same, if I create one Subject
, then anywhere in the code can get to this reference, you can freely use it to emit elements, the consequences of abuse caused the code more and more difficult to maintain, I do not know whether the other people in some I do not know the launch of the element I do not know, I don't believe anyone would want to maintain such a code. This is an anti-pattern, the same as the C language when the idea of modularity has not yet been rooted in the global variables brought about by the same disaster.
Perhaps see here you will think, said for a long while seems to return to the beginning, Subject
with the flexibility of programming is not recommended, for these reasons and re-use the three inflexible factory method, really can not meet the demand ah. Let's look back at some of the most common realities of programming that we've mentioned before:
Events that the user interacts with the UI
Changes in the type of mobile device network (WiFi vs. cellular network switching)
Arrival of the server push message
In fact, these events are often provided to the programmer on the interface of the registered listener, and we can use Observable.create
this factory method to create the observable:
FinalClass Viewclickonsubscribe implements Observable.onsubscribe<void> {FinalView view; Viewclickonsubscribe (view view) { This. view = view; }@Override Public void Pager(Finalsubscriber<?Supervoid> subscriber) {verifymainthread (); View.onclicklistener listener =NewView.onclicklistener () {@Override Public void OnClick(View v) {if(!subscriber.isunsubscribed ()) {Subscriber.onnext (NULL); } } }; View.setonclicklistener (listener); Subscriber.add (NewMainthreadsubscription () {@Override protected void Onunsubscribe() {View.setonclicklistener (NULL); } }); }}
The above code comes from Jake Wharton's Android Project Rxbinding, which is designed to translate the events generated by user interaction with controls on the Android UI into a Observable
programmer. The idea of the above code is simple, that is, when there is a Subscriber
click event that you want to subscribe to View
, it is a View
callback () that registers a click in the Android Framework, which is view.setOnClickListener(listener)
called Subscriber
onNext
whenever the Click event arrives. Method.
Let's compare another less-than-good notation:
new View.OnClickListener() { @OverridepublicvoidonClick(View v) { subject.onNext(null); }};view.setOnClickListener(listener);
Here is subject
just the entire project part of the code, we do not know whether other places to subject
give the object, the potential risk is that we have just discussed may miss the critical situation of the event , thread insecurity , The event source is unpredictable.
Summarize
We've learned about the Subject
flexibility and risk we have, so when it comes to real-world projects, I recommend using the Observable
3 factory methods provided, and using them sparingly Subject
, 90% of the cases can be solved using the 3 factory methods, if you're sure you want to use Subject
, then make sure: 1. This is one Hot Observable
and you have a corresponding measure to ensure that the critical event is not missed; 2. There is a corresponding thread security; 3. Modular code to ensure that the source of the event is in control and the event is sent fully predictable. Yes, plus the necessary notes:)
RxJava common Misunderstanding (a): excessive use of Subject