Error handling
So far, we haven't introduced the OnComplete () and onerror () functions. These functions are used to inform subscribers that the observed object will stop sending the data and why it has stopped (successful or wrong).
The following code shows how to use these two functions:
Observable.just ("Hello, world!")
. Map (s-> potentialexception (s))
. Map (S-> anotherpotentialexception (s))
. Subscribe (New subscriber< String> () {
@Override public
void OnNext (String s) {System.out.println (s);}
@Override public
void oncompleted () {System.out.println ("completed!");}
@Override public
void OnError (Throwable e) {System.out.println ("ouch!");}
);
Potentialexception () and anotherpotentialexception () in your code may throw an exception. Each Observerable object calls the OnCompleted () or OnError () method at the end, so "completed!" is printed in the demo or "ouch!".
This pattern has several advantages:
1. OnError () must be invoked whenever there is an anomaly
This greatly simplifies error handling. You just need to handle the error in one place.
2. Operator does not need to handle exceptions
To give exception handling to the Subscriber, the OnError () method is executed directly when one of the observerable operator call chains throws an exception.
3. You can tell when the Subscriber has received all the data.
Knowing when a task is over can help streamline the process of the code. (although it is possible that observable objects will never end)
I think this type of error handling is simpler than traditional error handling. In traditional error handling, errors are usually handled in each callback. This not only leads to duplicate code, but also means that each callback must know how to handle the error, and your callback code will be tightly coupled with the caller.
Using the Rxjava,observable object does not need to know how to handle the error at all! The operator also does not need to handle the error state-Once an error occurs, the current and subsequent operators are skipped. All error handling is given to the subscriber.
Dispatch device
Suppose you're writing an Android app that needs data from the Web (it feels like it's a must, is there a single machine?). )。 The network request takes a long time, so you intend to load the data in another thread. So here's the problem!
It's hard to write multithreaded Android apps, because you have to make sure that the code runs in the right thread, or else it could crash the app. The most common is to update the UI on a non main thread.
With Rxjava, you can use the Subscribeon () to specify the thread that the watcher code runs, using Observeron () to specify the thread that the Subscriber runs:
Myobservableservices.retrieveimage (URL)
. Subscribeon (Schedulers.io ())
. Observeon ( Androidschedulers.mainthread ())
. Subscribe (bitmap-> myimageview.setimagebitmap (bitmap));
Is it simple? Any code that executes before my subscriber is run in an I/O thread. Finally, the code that operates the view runs in the main thread.
The best part is that I can add Subscribeon () and Observeron () to any observable object. These two are also operators!. I don't need to care about the observable object and what operators it has on it. Only using these two operators can be implemented in different threads of scheduling.
If you use Asynctask or something like that, I will have to carefully design my code to find out what part of it needs to be executed concurrently. With Rxjava, I can keep the code unchanged, just call the two operators when concurrency is needed.
Subscribe to (subscriptions)
When Observable.subscribe () is invoked, a subscription object is returned. This object represents the relationship between the observer and the Subscriber.
Ubscription subscription = Observable.just ("Hello, world!")
. Subscribe (s-> System.out.println (s));
You can use this subscription object later to manipulate the relationship between the observer and the Subscriber.
Subscription.unsubscribe ();
System.out.println ("unsubscribed=" + subscription.isunsubscribed ());
Outputs "Unsubscribed=true"
Another benefit of Rxjava is that it stops the entire call chain when it processes unsubscribing. If you use a string of very complex operators, calling Unsubscribe will terminate at the place where he is currently executing. No need to do any extra work!
Rxandroid
Rxandroid is an extension of Rxjava for the Android platform. It includes tools to simplify the development of Android.
First, Androidschedulers provides a scheduler for the Android thread system. Need to run some code in the UI thread? Simple enough to use Androidschedulers.mainthread ():
Retrofitservice.getimage (URL)
. Subscribeon (Schedulers.io ())
. Observeon (Androidschedulers.mainthread ())
. Subscribe (bitmap-> myimageview.setimagebitmap (bitmap));
If you have created your own handler, you can use HandlerThreadScheduler1 to link a scheduler to your handler.
The next thing to introduce is androidobservable, which provides a lot of functionality to match the Android lifecycle. The Bindactivity () and Bindfragment () methods use Androidschedulers.mainthread () to perform the observer code by default. These two methods will notify the observer to stop sending new messages at the end of the activity or fragment.
Androidobservable.bindactivity (this, retrofitservice.getimage (URL))
. Subscribeon (Schedulers.io ())
. Subscribe (bitmap-> myimageview.setimagebitmap (bitmap);
I also like the Androidobservable.frombroadcast () method, which allows you to create a observable object similar to Broadcastreceiver. The following example shows how to be notified when a network changes:
Intentfilter filter = new Intentfilter (connectivitymanager.connectivity_action);
Androidobservable.frombroadcast (context, filter)
. Subscribe (Intent-> Handleconnectivitychange (intent));
The last thing to introduce is viewobservable, which you can use to add some bindings to the view. If you want to receive an event each time you click View, you can use Viewobservable.clicks (), or you want to monitor the TextView content changes, you can use Viewobservable.text ().
Viewobservable.clicks (Mcardnameedittext, False)
. Subscribe (View-> handleclick (view));
Retrofit
The famous retrofit library has built-in support for Rxjava (the official download page http://square.github.io/retrofit/#download). Typically, a call can get an asynchronous result by using a callback object:
@GET ("/user/{id}/photo")
void Getuserphoto (@Path ("id") int ID, callback<photo> CB);
Using Rxjava, you can return directly to a observable object.
@GET ("/user/{id}/photo")
observable<photo> Getuserphoto (@Path ("id") int id);
Now you are free to use the observable object. Not only can you get the data, but you can also transform it.
Retrofit's support for observable makes it easy to combine multiple rest requests. For example, we have a request to get the photos, and a request is to get the metadata, we can send the two requests concurrent, and wait until two results are returned before processing:
Observable.zip (
Service.getuserphoto (ID),
service.getphotometadata (ID),
(photo, metadata)-> Createphotowithdata (photo, metadata))
. Subscribe (photowithdata-> (Showphoto));
In the second chapter I have shown a similar example (using Flatmap ()). Here I just want to show how easy it is to combine multiple rest requests using Rxjava+retrofit.
Legacy code, running very slow code
Retrofit can return observable objects, but what if the other libraries you use do not support this? or an internal inner code, you want to convert them into observable? Is there any easy way to do that?
Most of the time Observable.just () and Observable.from () can help you create observable objects from legacy code:
Private Object Oldmethod () {...}
Public observable<object> Newmethod () {return
observable.just (Oldmethod ());
}
In the example above, if Oldmethod () is fast enough, it's not a problem, but what if it's slow? Calling Oldmethod () will block the thread in which he resides.
To solve this problem, refer to the method I have been using-using defer () to wrap the slow code:
Private Object Slowblockingmethod () {...}
Public observable<object> Newmethod () {return
observable.defer ()-> observable.just ( Slowblockingmethod ());
}
Now, the Newmethod () call will not block unless you subscribe to the returned observable object.
Life cycle
I left the hardest points in the end. How do you deal with the life cycle of an activity? There are two main questions:
1. After the configuration change (such as the turn screen) to continue before the subscription.
For example, you use retrofit to send out a rest request, and then you want to show the results in ListView. What if the user spins the screen when the network requests it? Of course you want to continue the request, but how?
2.Observable memory leak due to context
The problem is that when you create a subscription, you hold a reference to the context in some way, especially when you interact with the view, which is too easy to happen! If the observable does not end in time, the memory footprint will be more and more large.
Unfortunately, there is no silver bullet to solve these two problems, but here are some guidelines you can refer to.
The solution to the first problem is to use the Rxjava built-in caching mechanism so that you can perform unsubscribe/resubscribe on the same observable object without having to run the observable code repeatedly. The cache () (or Replay ()) will continue to execute the network request (even if you call unsubscribe will not stop). This means that you can create a new observable object from the return value of cache () when the activity is recreated.
observable<photo> request = Service.getuserphoto (ID). cache ();
Subscription sub = request.subscribe (Photo-> Handleuserphoto (photo));
// ... When the being recreated
... Sub.unsubscribe ();
// ... Once the activity is recreated
... Request.subscribe (Photo-> Handleuserphoto (photo));
Note that the two sub is the same cache request used. Of course where to store the result of the request or do it yourself, and all other lifecycle-related solutions A tiger must be stored somewhere outside the lifecycle. (retained fragment or single example, etc.).
The solution to the second problem is to unsubscribe at some point in the lifecycle. A very common pattern is to use compositesubscription to hold all the subscriptions and then cancel all subscriptions in OnDestroy () or Ondestroyview ().
Private compositesubscription mcompositesubscription
= new Compositesubscription ();
private void DoSomething () {Mcompositesubscription.add (androidobservable.bindactivity) (This
, Observable.just ("Hello, world!"))
. Subscribe (s-> System.out.println (s)));
}
@Override
protected void OnDestroy () {
Super.ondestroy ();
Mcompositesubscription.unsubscribe ();
}
You can create a Compositesubscription object in the base class of activity/fragment and use it in subclasses.
Attention! Once you call Compositesubscription.unsubscribe (), the Compositesubscription object is unavailable, and if you still want to use compositesubscription, You have to create a new object.